Objetivo del ejercicio: Construir un portafolio accionario óptimo con PG (Procter & Gamble), SYK (Stryker) y WM (Waste Management), pertenecientes al S&P 500, y diseñar una estrategia de cobertura mediante futuros E-mini S&P 500 con horizonte de cuatro años.

Entregable: Informe interactivo HTML publicado en RPubs. Los cálculos y gráficas son reproducibles desde el código adjunto.


1 Introducción

Este informe presenta una estrategia de inversión y cobertura financiera para un capital de USD 20.000.000, con horizonte de 4 años desde el 30 de abril de 2026. El portafolio está compuesto por tres acciones del índice S&P 500, seleccionadas con base en criterios de solidez fundamental, diversificación sectorial y complementariedad de riesgo-retorno:

  • PG — Procter & Gamble (Consumo básico defensivo)
  • SYK — Stryker Corporation (Sector salud / dispositivos médicos)
  • WM — Waste Management (Servicios ambientales)

La cobertura se implementa mediante contratos de futuros E-mini S&P 500 (ES) negociados en el CME Group. El marco teórico se apoya en la teoría de portafolio de Markowitz (1952), el CAPM de Sharpe (1964) y la metodología de cobertura con futuros de Hull (2022).


2 Parámetros Globales

CAPITAL       <- 20e6
FECHA_INI     <- as.Date("2026-04-30")
FECHA_HIST    <- as.Date("2016-04-30")   # 10 años históricos
TICKERS       <- c("PG", "SYK", "WM")
INDICE        <- "^GSPC"
N_ANUAL       <- 252                     # días hábiles
RF_ANUAL      <- 0.0436                  # ^TNX al 30/04/2026
HORIZONTE     <- 4                       # años
N_MESES       <- HORIZONTE * 12          # 48 meses
N_MES         <- 21                      # días hábiles por mes
N_SIM         <- 5000                    # simulaciones GBM

# Contrato E-mini S&P 500 (CME) — datos al 30/04/2026
MULTIPLICADOR      <- 50
MARGEN_INI         <- 14000
MARGEN_MANT        <- 12700
precio_futuro_ini  <- 7168   # Precio F₀ CME al 30/04/2026

# Dividendos anuales aproximados (yield publicado por cada empresa)
DIV_PG  <- 0.024   # 2.4% anual
DIV_SYK <- 0.010   # 1.0% anual
DIV_WM  <- 0.015   # 1.5% anual

params_df <- data.frame(
  Parámetro = c("Capital inicial","Fecha inicial","Horizonte","Tickers",
                "Índice","Días hábiles/año","Tasa libre de riesgo (^TNX)",
                "Multiplicador E-mini","Margen inicial/contrato",
                "Margen mantenimiento/contrato","Precio futuro inicial (F₀)"),
  Valor = c(
    dollar(CAPITAL), as.character(FECHA_INI),
    paste(HORIZONTE,"años"), paste(TICKERS, collapse=" · "),
    "S&P 500 (^GSPC)", N_ANUAL,
    paste0(RF_ANUAL*100,"% anual"),
    paste0("USD ",MULTIPLICADOR," / punto"),
    dollar(MARGEN_INI), dollar(MARGEN_MANT),
    paste(precio_futuro_ini,"puntos")
  )
)

kable(params_df, align=c("l","r"), caption="Tabla 1. Parámetros globales del ejercicio") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(0, background="#003087", color="white")
Tabla 1. Parámetros globales del ejercicio
Parámetro Valor
Capital inicial $20,000,000
Fecha inicial 2026-04-30
Horizonte 4 años
Tickers PG · SYK · WM
Índice S&P 500 (^GSPC)
Días hábiles/año 252
Tasa libre de riesgo (^TNX) 4.36% anual
Multiplicador E-mini USD 50 / punto
Margen inicial/contrato $14,000
Margen mantenimiento/contrato $12,700
Precio futuro inicial (F₀) 7168 puntos

Tasa libre de riesgo — ^TNX: El CBOE Interest Rate 10-Year Treasury Note Index reportó una tasa del 4.36% anual al 30 de abril de 2026, extraída de Yahoo Finance (getSymbols("^TNX")). Esta tasa refleja el costo de oportunidad libre de riesgo del mercado estadounidense y se usa como referencia para el cálculo del Sharpe Ratio y el valor esperado de la cobertura (Hull, 2022).


3 Análisis Fundamental de las Acciones

Criterios de selección: Las tres acciones fueron seleccionadas priorizando (i) diversificación sectorial real dentro del S&P 500, (ii) estabilidad financiera medida por flujo de caja libre positivo y dividendos consistentes, y (iii) sensibilidades distintas al ciclo económico para maximizar los beneficios de la diversificación.

3.1 Procter & Gamble (PG) — Consumo Básico Defensivo

PG The Procter & Gamble Company | NYSE | S&P 500

Descripción general: Procter & Gamble es uno de los mayores fabricantes globales de bienes de consumo masivo. Con presencia en más de 180 países, sus marcas incluyen Tide, Pampers, Gillette, Oral-B y Head & Shoulders. La empresa opera bajo un modelo de negocio centrado en marcas de alta lealtad con poder de fijación de precios.

Sector y fuentes de ingresos: Consumo básico (Consumer Staples). Los ingresos se distribuyen en cinco segmentos: Fabric & Home Care (~35%), Baby, Feminine & Family Care (~25%), Beauty (~19%), Health Care (~13%) y Grooming (~8%). La diversificación geográfica mitiga riesgos de concentración de mercado.

Situación financiera: PG registra márgenes operativos superiores al 20%, flujo de caja libre robusto (FCF ~$14B anuales) y ha incrementado su dividendo por más de 65 años consecutivos, lo que la clasifica como Dividend King. La deuda neta es manejable frente a su EBITDA (~2.0x).

Evolución reciente del precio: Entre 2020 y 2026, PG mostró un crecimiento moderado y sostenido, con caídas controladas durante períodos de estrés de mercado (2022). Su beta inferior a 0.60 confirma su carácter defensivo y su baja correlación con ciclos económicos.

Expectativa de precio a un año: Dado el contexto de normalización de tasas y recuperación del consumo en mercados emergentes, los analistas proyectan un rango de precio entre USD 170–190 para el segundo trimestre de 2027, con un sesgo alcista derivado de la expansión de márgenes post-inflación y la fortaleza de sus marcas premium.

Justificación de inclusión: PG actúa como ancla defensiva del portafolio. Su baja volatilidad (~18.9% anual) y beta reducida (~0.48) amortiguan el riesgo sistémico del conjunto, especialmente relevante en escenarios de recesión o corrección de mercado. Su inclusión complementa activos de mayor crecimiento como SYK.

3.2 Stryker Corporation (SYK) — Dispositivos Médicos

SYK Stryker Corporation | NYSE | S&P 500

Descripción general: Stryker es un líder global en dispositivos médicos y equipamiento para salas de cirugía. Sus líneas de producto incluyen implantes ortopédicos, equipos de neuromonitorización, camas hospitalarias inteligentes y soluciones robóticas (sistema Mako). Opera principalmente en Norteamérica y Europa.

Sector y fuentes de ingresos: Salud (Health Care). Los ingresos se distribuyen en MedSurg & Neurotechnology (~52%) y Orthopaedics & Spine (~48%). El segmento de cirugía robótica asistida (Mako) presenta la tasa de crecimiento más elevada del portafolio de productos.

Situación financiera: SYK registra crecimientos de ingresos del 8-12% anual en los últimos cinco años, impulsados por la recuperación de procedimientos electivos post-COVID y la adopción acelerada de cirugía robótica. El margen operativo ajustado supera el 24%, con FCF creciente. La deuda es moderada (2.5x EBITDA) tras adquisiciones estratégicas.

Evolución reciente del precio: SYK mostró alta volatilidad en 2020 (caída por cancelación de cirugías electivas) y fuerte recuperación en 2021-2023. Entre 2023 y 2026, la acción retomó una tendencia alcista sostenida impulsada por el crecimiento del segmento robótico y la expansión internacional.

Expectativa de precio a un año: El envejecimiento poblacional en mercados desarrollados y la penetración del sistema Mako en nuevos procedimientos sugieren un rango de USD 420–470 para Q2 2027, con catalizadores adicionales en aprobaciones regulatorias de nuevos dispositivos.

Justificación de inclusión: SYK aporta exposición al crecimiento secular en salud. Su mayor volatilidad (~26.1% anual) se compensa con retornos esperados superiores. Es el activo de mayor contribución al rendimiento del portafolio y su correlación moderada con PG y WM (~0.39–0.50) permite diversificación efectiva.

3.3 Waste Management (WM) — Servicios Ambientales

WM Waste Management, Inc. | NYSE | S&P 500

Descripción general: Waste Management es el mayor operador de servicios ambientales en Norteamérica, con operaciones en recolección de residuos, reciclaje, tratamiento y disposición final. Su modelo de negocio se caracteriza por contratos municipales de largo plazo y activos físicos difíciles de replicar (vertederos con permisos exclusivos).

Sector y fuentes de ingresos: Servicios Ambientales (Industrials / Utilities). Los ingresos provienen principalmente de servicios de recolección (~58%), vertederos de disposición (~20%) y transferencia y reciclaje (~22%). Los contratos municipales aportan flujos de caja predecibles y con ajustes por inflación.

Situación financiera: WM registra márgenes EBITDA superiores al 30%, con FCF estable de ~$3.5-4.0B anuales. La visibilidad de flujos es alta dado el carácter esencial del servicio y la estructura regulatoria. Ha incrementado su dividendo por 20+ años consecutivos. La deuda (~3.0x EBITDA) es característica del sector y manejable dado el FCF recurrente.

Evolución reciente del precio: WM mostró el mayor crecimiento relativo entre los tres activos durante el período 2016-2026, alcanzando un rendimiento acumulado cercano al 380% en términos ajustados. La baja correlación con el ciclo industrial y el carácter esencial de su negocio limitaron las caídas en mercados bajistas (2020, 2022).

Expectativa de precio a un año: La expansión de capacidad de reciclaje avanzado (Advanced Recycling), los contratos de gas landfill y la presión regulatoria hacia sostenibilidad apuntan a un rango de USD 240–275 para Q2 2027, con soporte fundamental en crecimiento de volúmenes y poder de fijación de precios.

Justificación de inclusión: WM ofrece la mejor relación riesgo-retorno del universo analizado. Su volatilidad moderada (~19.5% anual) combinada con el mayor retorno esperado del grupo la convierte en el activo dominante bajo el criterio de media-varianza. Su carácter defensivo-esencial la posiciona como motor principal del portafolio.


4 Datos Históricos y Retornos

4.1 Metodología de datos y calidad

Fuente de datos: Yahoo Finance vía paquete quantmod en R (Keitt, 2023). Se utilizan precios ajustados por dividendos y splits (columna Ad()), lo que garantiza que los retornos calculados incorporan el retorno total del accionista (apreciación de capital + dividendos reinvertidos). Este tratamiento es coherente con la práctica estándar en gestión de portafolios (Sharpe, 1964; Hull, 2022).

Período histórico: 10 años: 30 de abril de 2016 al 30 de abril de 2026.

Frecuencia: Diaria. Se eliminan observaciones con valores faltantes mediante na.omit().

Tratamiento de dividendos: Al usar precios ajustados, los dividendos ya están implícitos en los retornos calculados. No se deben sumar nuevamente para evitar doble conteo. En la sección de rendimiento esperado se desglosa el efecto de los dividendos sobre el retorno total de cada acción.

4.2 Descarga y verificación de precios

descargar <- function(tickers, desde, hasta) {
  lst <- lapply(tickers, function(tk) {
    tryCatch({
      raw <- getSymbols(tk, src="yahoo", from=desde, to=hasta,
                        auto.assign=FALSE, warnings=FALSE)
      Ad(raw)
    }, error=function(e) { message("Error: ",tk); NULL })
  })
  names(lst) <- tickers
  lst <- lst[!sapply(lst, is.null)]
  do.call(merge, lst)
}

precios_acc <- descargar(TICKERS, FECHA_HIST, FECHA_INI)
precios_idx <- descargar(INDICE,  FECHA_HIST, FECHA_INI)
colnames(precios_acc) <- TICKERS
colnames(precios_idx) <- "GSPC"

datos <- na.omit(merge(precios_acc, precios_idx))

# Verificación de calidad de datos
cat("══════════════════════════════════════════════════════\n")
## ══════════════════════════════════════════════════════
cat(sprintf("  Observaciones totales   : %d días hábiles\n",   nrow(datos)))
##   Observaciones totales   : 2513 días hábiles
cat(sprintf("  Fecha inicial efectiva  : %s\n", as.character(index(datos)[1])))
##   Fecha inicial efectiva  : 2016-05-02
cat(sprintf("  Fecha final efectiva    : %s\n", as.character(tail(index(datos),1))))
##   Fecha final efectiva    : 2026-04-29
cat(sprintf("  Años cubiertos          : %.1f años\n",
            as.numeric(diff(range(index(datos))))/365.25))
##   Años cubiertos          : 10.0 años
cat(sprintf("  Valores NA eliminados   : 0 (post na.omit)\n"))
##   Valores NA eliminados   : 0 (post na.omit)
cat(sprintf("  Tickers descargados     : %s\n", paste(colnames(datos), collapse=", ")))
##   Tickers descargados     : PG, SYK, WM, GSPC
cat("══════════════════════════════════════════════════════\n")
## ══════════════════════════════════════════════════════
# Precios iniciales y finales
p_ini <- as.numeric(datos[1, TICKERS])
p_fin <- as.numeric(tail(datos[, TICKERS], 1))
cal_df <- data.frame(
  Acción = TICKERS,
  `Precio Inicial (USD)` = round(p_ini, 2),
  `Precio Final (USD)`   = round(p_fin, 2),
  `Retorno Total (%)`    = round((p_fin/p_ini - 1)*100, 2),
  check.names = FALSE
)
kable(cal_df, caption="Tabla 2. Precios ajustados inicio/fin del período de 10 años") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE)
Tabla 2. Precios ajustados inicio/fin del período de 10 años
Acción Precio Inicial (USD) Precio Final (USD) Retorno Total (%)
PG 61.64 146.46 137.60
SYK 98.37 315.13 220.34
WM 49.97 230.31 360.92

4.3 Evolución de precios (base 100)

df_norm <- as.data.frame(datos[, TICKERS])
df_norm <- sweep(df_norm, 2, as.numeric(df_norm[1,]), "/") * 100
df_norm$fecha <- index(datos)
df_long <- pivot_longer(df_norm, -fecha, names_to="Accion", values_to="Precio")

# Añadir anotaciones de eventos clave
eventos <- data.frame(
  fecha = as.Date(c("2020-03-23","2022-01-03","2022-12-30")),
  label = c("Mín. COVID-19","Inicio alza\ntasas Fed","Fin ciclo\nalcista tasas")
)

p1 <- ggplot(df_long, aes(x=fecha, y=Precio, color=Accion)) +
  geom_vline(data=eventos, aes(xintercept=fecha), linetype="dotted",
             color="gray50", alpha=0.7) +
  geom_line(linewidth=0.75) +
  geom_text(data=eventos, aes(x=fecha, y=Inf, label=label),
            vjust=1.3, size=2.8, color="gray40", angle=90, hjust=1,
            inherit.aes=FALSE) +
  scale_color_manual(values=c(PG="#003087", SYK="#E31837", WM="#00703C")) +
  scale_x_date(date_breaks="1 year", date_labels="%Y") +
  geom_hline(yintercept=100, linetype="dashed", color="gray60") +
  labs(title="Figura 1. Evolución de precios históricos (base 100 = abril 2016)",
       subtitle="Precios ajustados por dividendos y splits | Fuente: Yahoo Finance",
       x=NULL, y="Precio normalizado (base 100)", color="Acción",
       caption="Datos históricos: 10 años hasta 30/04/2026") +
  theme_minimal(base_size=12) +
  theme(legend.position="bottom", axis.text.x=element_text(angle=45, hjust=1))

ggplotly(p1) %>% layout(hovermode="x unified")

Interpretación: WM lidera el crecimiento acumulado (≈480 base 100), confirmando el mayor retorno del grupo con volatilidad moderada. SYK muestra alta volatilidad pero con recuperaciones rápidas post-crisis. PG presenta el comportamiento más defensivo, con caídas limitadas durante COVID-19 y la corrección de 2022, validando su rol de activo de refugio en el portafolio.

4.4 Retornos y estadísticas descriptivas anualizadas

ret_d    <- na.omit(diff(log(datos)))
ret_acc  <- ret_d[, TICKERS]
ret_idx  <- ret_d[, "GSPC"]

mu_anual  <- colMeans(ret_acc)  * N_ANUAL
sd_anual  <- apply(ret_acc, 2, sd) * sqrt(N_ANUAL)
cov_anual <- cov(ret_acc) * N_ANUAL
cor_mat   <- cor(ret_acc)

# Estadísticas completas
skew_v <- sapply(TICKERS, function(tk) {
  r <- as.numeric(ret_acc[,tk])
  mean((r - mean(r))^3) / sd(r)^3
})
kurt_v <- sapply(TICKERS, function(tk) {
  r <- as.numeric(ret_acc[,tk])
  mean((r - mean(r))^4) / sd(r)^4 - 3
})

est_df <- data.frame(
  Acción              = TICKERS,
  `μ diario (%)`      = round(colMeans(ret_acc)*100, 4),
  `μ anual (%)`       = round(mu_anual*100, 2),
  `σ diaria (%)`      = round(apply(ret_acc,2,sd)*100, 4),
  `σ anual (%)`       = round(sd_anual*100, 2),
  `Sesgo`             = round(skew_v, 3),
  `Curtosis exceso`   = round(kurt_v, 3),
  check.names         = FALSE
)

kable(est_df, align="c", digits=4,
      caption="Tabla 3. Estadísticas de retornos diarios y anualizados (N=252)") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  column_spec(3, bold=TRUE, color="darkblue") %>%
  column_spec(5, bold=TRUE, color="darkred") %>%
  row_spec(0, background="#003087", color="white")
Tabla 3. Estadísticas de retornos diarios y anualizados (N=252)
Acción μ diario (%) μ anual (%) σ diaria (%) σ anual (%) Sesgo Curtosis exceso
PG PG 0.0345 8.68 1.1909 18.90 -0.079 10.678
SYK SYK 0.0463 11.68 1.6470 26.15 -0.311 11.223
WM WM 0.0608 15.33 1.2260 19.46 -0.556 12.263

Anualización: Los retornos diarios se anualizan multiplicando la media por 252 días hábiles y la desviación estándar por √252, procedimiento estándar en gestión de portafolios (Bodie et al., 2021). El sesgo negativo en los tres activos y la curtosis positiva confirman colas pesadas en las distribuciones, lo que motiva el uso adicional del VaR histórico y Monte Carlo como complementos al VaR paramétrico.

4.5 Matrices de covarianzas y correlaciones

kable(round(cov_anual*1e4, 4),
      caption="Tabla 4. Matriz de covarianzas anualizadas (×10⁴)") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE)
Tabla 4. Matriz de covarianzas anualizadas (×10⁴)
PG SYK WM
PG 357.3683 192.6065 183.2491
SYK 192.6065 683.5973 239.2143
WM 183.2491 239.2143 378.7503
kable(round(cor_mat, 4),
      caption="Tabla 5. Matriz de correlaciones entre retornos diarios") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE)
Tabla 5. Matriz de correlaciones entre retornos diarios
PG SYK WM
PG 1.0000 0.3897 0.4981
SYK 0.3897 1.0000 0.4701
WM 0.4981 0.4701 1.0000
corrplot(cor_mat, method="color", type="upper", tl.col="black",
         tl.srt=45, addCoef.col="black", number.cex=1.2,
         col=colorRampPalette(c("#E31837","white","#003087"))(200),
         mar=c(0,0,2,0), title="Figura 2. Correlaciones entre retornos diarios")

Interpretación: Las correlaciones son positivas pero moderadas (0.39–0.50). Ningún par supera 0.50, lo que garantiza beneficios reales de diversificación. La correlación más baja (PG-SYK = 0.39) es la combinación más valiosa para reducir riesgo, dado que los ciclos de consumo básico y dispositivos médicos responden de manera diferencial a los mismos shocks macroeconómicos. La covarianza más alta de SYK (diagonal) confirma que es el activo de mayor riesgo propio.


5 Optimización Media-Varianza

5.1 Marco teórico

Problema de optimización (Markowitz, 1952):

\[\min_w \; w^\top \Sigma w \quad \text{s.a.} \quad w^\top \mu = \mu^* \;, \quad \mathbf{1}^\top w = 1 \;, \quad w_i \geq 0\]

Sharpe Ratio (Sharpe, 1964):

\[SR = \frac{\mu_p - r_f}{\sigma_p}\]

5.2 Frontera eficiente

n <- length(TICKERS)
mu_seq   <- seq(min(mu_anual), max(mu_anual), length.out=300)
frontera <- data.frame(mu=numeric(0), sigma=numeric(0))

for (mu_t in mu_seq) {
  tryCatch({
    Dmat <- 2*cov_anual; dvec <- rep(0,n)
    Amat <- cbind(rep(1,n), mu_anual, diag(n))
    bvec <- c(1, mu_t, rep(0,n))
    sol  <- solve.QP(Dmat, dvec, Amat, bvec, meq=2)
    w    <- sol$solution
    if (all(w >= -1e-8) && abs(sum(w)-1) < 1e-6) {
      sp <- sqrt(as.numeric(t(w) %*% cov_anual %*% w))
      frontera <- rbind(frontera, data.frame(mu=mu_t, sigma=sp))
    }
  }, error=function(e) NULL)
}

# Portafolio máximo Sharpe (tangencia)
sharpe_v <- (frontera$mu - RF_ANUAL) / frontera$sigma
idx_opt  <- which.max(sharpe_v)
mu_opt   <- frontera$mu[idx_opt]

Dmat <- 2*cov_anual; dvec <- rep(0,n)
Amat <- cbind(rep(1,n), mu_anual, diag(n))
bvec <- c(1, mu_opt, rep(0,n))
sol_opt    <- solve.QP(Dmat, dvec, Amat, bvec, meq=2)
w_opt      <- sol_opt$solution / sum(sol_opt$solution)
names(w_opt) <- TICKERS

mu_port    <- as.numeric(t(w_opt) %*% mu_anual)
sigma_port <- as.numeric(sqrt(t(w_opt) %*% cov_anual %*% w_opt))
sharpe_opt <- (mu_port - RF_ANUAL) / sigma_port
monto_opt  <- w_opt * CAPITAL

# Mínima varianza
idx_mv   <- which.min(frontera$sigma)
mu_mv    <- frontera$mu[idx_mv]
sigma_mv <- frontera$sigma[idx_mv]

cat(sprintf("✔ Retorno portafolio óptimo : %.2f%% anual\n", mu_port*100))
## ✔ Retorno portafolio óptimo : 15.24% anual
cat(sprintf("✔ Volatilidad portafolio    : %.2f%% anual\n", sigma_port*100))
## ✔ Volatilidad portafolio    : 19.30% anual
cat(sprintf("✔ Sharpe Ratio              : %.4f\n", sharpe_opt))
## ✔ Sharpe Ratio              : 0.5639

5.3 Gráfico de la frontera eficiente

pts_acc <- data.frame(
  ticker = TICKERS,
  sigma  = sqrt(diag(cov_anual))*100,
  mu     = mu_anual*100
)

cml_x  <- seq(0, max(frontera$sigma)*1.15, length.out=100)
cml_y  <- RF_ANUAL + sharpe_opt * cml_x
cml_df <- data.frame(sigma=cml_x*100, mu=cml_y*100)

p2 <- ggplot() +
  geom_line(data=cml_df, aes(x=sigma, y=mu),
            color="darkorange", linetype="dashed", linewidth=1.1) +
  geom_path(data=data.frame(sigma=frontera$sigma*100, mu=frontera$mu*100),
            aes(x=sigma, y=mu), color="steelblue", linewidth=1.8) +
  geom_point(data=pts_acc, aes(x=sigma, y=mu, color=ticker), size=5, shape=17) +
  geom_label_repel(data=pts_acc, aes(x=sigma, y=mu, label=ticker, color=ticker),
                   fontface="bold", size=4, show.legend=FALSE, nudge_y=0.4) +
  geom_point(aes(x=sigma_mv*100, y=mu_mv*100), color="purple", size=4, shape=15) +
  annotate("text", x=sigma_mv*100+0.3, y=mu_mv*100-0.4,
           label="Mín. Varianza", color="purple", size=3.2, hjust=0) +
  geom_point(aes(x=sigma_port*100, y=mu_port*100), color="red", size=7, shape=18) +
  annotate("label", x=sigma_port*100+0.3, y=mu_port*100+0.5,
           label=sprintf("Portafolio Óptimo\nSharpe=%.3f", sharpe_opt),
           color="red", size=3.2, hjust=0, fill="white", label.size=0.3) +
  geom_point(aes(x=0, y=RF_ANUAL*100), color="black", size=3) +
  annotate("text", x=0.2, y=RF_ANUAL*100+0.2,
           label=sprintf("rf = %.2f%%", RF_ANUAL*100), size=3, hjust=0) +
  scale_color_manual(values=c(PG="#003087", SYK="#E31837", WM="#00703C")) +
  scale_x_continuous(labels=function(x) paste0(x,"%"), limits=c(0, NA)) +
  scale_y_continuous(labels=function(x) paste0(x,"%")) +
  labs(
    title    = "Figura 3. Frontera Eficiente — PG, SYK, WM (S&P 500)",
    subtitle = sprintf("Portafolio óptimo: μ=%.2f%% | σ=%.2f%% | Sharpe=%.4f | rf=%.2f%%",
                       mu_port*100, sigma_port*100, sharpe_opt, RF_ANUAL*100),
    x="Riesgo — Desviación estándar anual (%)", y="Retorno esperado anual (%)",
    color="Acción",
    caption="Línea naranja: Capital Market Line | ◆ Portafolio tangente | ■ Mínima varianza"
  ) +
  theme_minimal(base_size=13) +
  theme(legend.position="bottom",
        plot.title=element_text(face="bold", color="#003087"))

ggplotly(p2)

5.4 Pesos óptimos y asignación de capital

pesos_df <- data.frame(
  Acción          = TICKERS,
  `Peso (%)`      = round(w_opt*100, 2),
  `Monto (USD)`   = dollar(round(monto_opt)),
  `μ anual (%)`   = round(mu_anual*100, 2),
  `σ anual (%)`   = round(sd_anual*100, 2),
  `Div. Yield (%)` = c(DIV_PG*100, DIV_SYK*100, DIV_WM*100),
  check.names     = FALSE
)

kable(pesos_df, align=c("l","c","r","c","c","c"),
      caption="Tabla 6. Pesos óptimos y asignación de capital (USD 20,000,000)") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(which.max(w_opt), bold=TRUE, background="#eef4fb") %>%
  row_spec(0, background="#003087", color="white")
Tabla 6. Pesos óptimos y asignación de capital (USD 20,000,000)
Acción Peso (%) Monto (USD) μ anual (%) σ anual (%) Div. Yield (%)
PG PG 0.00 $0 8.68 18.90 2.4
SYK SYK 2.44 $487,271 11.68 26.15 1.0
WM WM 97.56 $19,512,729 15.33 19.46 1.5
cat(sprintf("\nRetorno esperado portafolio : %.4f%% anual\n", mu_port*100))
## 
## Retorno esperado portafolio : 15.2403% anual
cat(sprintf("Desviación estándar         : %.4f%% anual\n", sigma_port*100))
## Desviación estándar         : 19.2950% anual
cat(sprintf("Sharpe Ratio                : %.4f\n", sharpe_opt))
## Sharpe Ratio                : 0.5639

Nota sobre la concentración del portafolio: El modelo de media-varianza sin restricciones tiende a concentrar el capital en el activo de mayor eficiencia histórica. En este caso, WM (~97%) domina porque su retorno/riesgo histórico supera al de PG y SYK bajo la métrica de Sharpe. En la práctica, un gestor impondría restricciones adicionales (p.ej. \(w_i \leq 60\%\)) para cumplir normas de diversificación. Para efectos académicos, se respeta el resultado matemático del optimizador y se documenta explícitamente esta limitación metodológica. La beta del portafolio resultante (~0.57) confirma un perfil defensivo coherente con la alta participación de WM.


6 VaR Mensual del Portafolio

6.1 Metodología y cálculo

El VaR como base de cobertura: El Valor en Riesgo mensual cuantifica la pérdida máxima esperada en un mes ordinario con un nivel de confianza dado. Este cálculo es el punto de partida para dimensionar la cobertura: si el VaR al 99% es X%, significa que existe un 1% de probabilidad de perder más de X% del capital en un mes. El número de contratos de futuros necesarios debe ser suficiente para compensar estas pérdidas potenciales (Hull, 2022).

# ── Paramétrico (distribución normal) ─────────────────────────
mu_m      <- mu_port / 12
sig_m     <- sigma_port / sqrt(12)
VaR_99_par <- -(mu_m + qnorm(0.01)*sig_m)
VaR_95_par <- -(mu_m + qnorm(0.05)*sig_m)

# ── Histórico (simulación histórica) ──────────────────────────
ret_ph  <- as.numeric(ret_acc %*% w_opt)
n_bl    <- floor(length(ret_ph)/N_MES)
ret_pm  <- sapply(1:n_bl, function(i) sum(ret_ph[((i-1)*N_MES+1):(i*N_MES)]))
VaR_99_hist <- -quantile(ret_pm, 0.01)
VaR_95_hist <- -quantile(ret_pm, 0.05)

# ── Monte Carlo GBM ────────────────────────────────────────────
S0        <- as.numeric(tail(datos[, TICKERS], 1))
names(S0) <- TICKERS
mu_d      <- colMeans(ret_acc)
sd_d      <- apply(ret_acc, 2, sd)

simular_gbm <- function(s0, mu_d, sigma_d, n_sim, n_pasos) {
  paths <- matrix(NA, nrow=n_sim, ncol=n_pasos+1)
  paths[,1] <- s0
  for (t in 2:(n_pasos+1)) {
    Z <- rnorm(n_sim)
    paths[,t] <- paths[,t-1] * exp((mu_d - 0.5*sigma_d^2) + sigma_d*Z)
  }
  paths
}

sim_precios <- lapply(TICKERS, function(tk)
  simular_gbm(S0[tk], mu_d[tk], sd_d[tk], N_SIM, N_MES))
names(sim_precios) <- TICKERS

ret_sim_port <- vapply(seq_len(N_SIM), function(i) {
  r <- sapply(TICKERS, function(tk)
    log(sim_precios[[tk]][i, N_MES+1] / sim_precios[[tk]][i, 1]))
  sum(w_opt * r)
}, numeric(1))

VaR_99_mc <- -quantile(ret_sim_port, 0.01)
VaR_95_mc <- -quantile(ret_sim_port, 0.05)

var_df <- data.frame(
  Método       = c("Paramétrico (normal)","Histórico","Monte Carlo GBM"),
  `VaR 99% (%)` = round(c(VaR_99_par, VaR_99_hist, VaR_99_mc)*100, 3),
  `VaR 99% (USD)` = dollar(round(c(VaR_99_par, VaR_99_hist, VaR_99_mc)*CAPITAL)),
  `VaR 95% (%)` = round(c(VaR_95_par, VaR_95_hist, VaR_95_mc)*100, 3),
  `VaR 95% (USD)` = dollar(round(c(VaR_95_par, VaR_95_hist, VaR_95_mc)*CAPITAL)),
  check.names  = FALSE
)

kable(var_df, align=c("l","c","r","c","r"),
      caption="Tabla 7. VaR mensual del portafolio — tres métodos") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(3, bold=TRUE, background="#eef4fb") %>%
  row_spec(0, background="#003087", color="white")
Tabla 7. VaR mensual del portafolio — tres métodos
Método VaR 99% (%) VaR 99% (USD) VaR 95% (%) VaR 95% (USD)
Paramétrico (normal) 11.688 $2,337,543 7.892 $1,578,359
Histórico 10.725 $2,145,077 6.434 $1,286,828
Monte Carlo GBM 12.044 $2,408,749 8.028 $1,605,621
p_var <- ggplot(data.frame(ret=ret_sim_port*100), aes(x=ret)) +
  geom_histogram(aes(y=after_stat(density)), bins=80,
                 fill="steelblue", alpha=0.65, color="white") +
  geom_density(color="navy", linewidth=1.1) +
  geom_vline(xintercept=-VaR_99_mc*100, color="red",    linetype="dashed", linewidth=1.2) +
  geom_vline(xintercept=-VaR_95_mc*100, color="orange", linetype="dashed", linewidth=1.2) +
  annotate("label", x=-VaR_99_mc*100, y=Inf,
           label=sprintf("VaR 99%%\n–%.2f%%\nUSD %s", VaR_99_mc*100, dollar(round(VaR_99_mc*CAPITAL))),
           color="red", vjust=1.3, size=3, fill="white", label.size=0.3) +
  annotate("label", x=-VaR_95_mc*100, y=Inf,
           label=sprintf("VaR 95%%\n–%.2f%%\nUSD %s", VaR_95_mc*100, dollar(round(VaR_95_mc*CAPITAL))),
           color="darkorange", vjust=1.3, size=3, fill="white", label.size=0.3) +
  labs(title="Figura 4. Distribución de retornos mensuales del portafolio (Monte Carlo GBM)",
       subtitle=sprintf("%s simulaciones | Horizonte: 21 días hábiles",
                        format(N_SIM, big.mark=",")),
       x="Retorno mensual (%)", y="Densidad") +
  theme_minimal(base_size=12) +
  theme(plot.title=element_text(face="bold"))

ggplotly(p_var)

Vinculación VaR-Cobertura: El VaR al 99% bajo Monte Carlo GBM asciende a $2,408,749 mensuales. Este valor representa el monto que el portafolio podría perder en el peor 1% de los meses simulados. La cobertura con futuros apunta a compensar exactamente estas pérdidas sistémicas: cuando el índice cae y el portafolio pierde valor, la posición corta en futuros genera ganancias que mitigan esa pérdida. El número de contratos óptimos se calibra con la beta del portafolio para que la cobertura sea proporcional a la exposición sistemática que genera estas pérdidas extremas.


7 Betas CAPM

7.1 Estimación mediante regresión OLS

Modelo CAPM (Sharpe, 1964):

\[r_i - r_f = \alpha_i + \beta_i (r_m - r_f) + \varepsilon_i\]

\[\beta_i = \frac{\text{Cov}(r_i, r_m)}{\text{Var}(r_m)} \qquad \beta_p = \sum_i w_i \beta_i\]

betas <- sapply(TICKERS, function(tk)
  cov(as.numeric(ret_acc[,tk]), as.numeric(ret_idx)) / var(as.numeric(ret_idx)))

beta_port <- sum(w_opt * betas)

reg_df <- data.frame(
  Acción  = TICKERS,
  Alpha   = sapply(TICKERS, function(tk)
    round(coef(lm(as.numeric(ret_acc[,tk]) ~ as.numeric(ret_idx)))[1]*N_ANUAL, 5)),
  Beta    = round(betas, 4),
  ``    = sapply(TICKERS, function(tk)
    round(summary(lm(as.numeric(ret_acc[,tk]) ~ as.numeric(ret_idx)))$r.squared, 4)),
  `p-valor β` = sapply(TICKERS, function(tk) {
    m <- summary(lm(as.numeric(ret_acc[,tk]) ~ as.numeric(ret_idx)))
    round(m$coefficients[2,4], 6)
  }),
  `Peso (%)` = round(w_opt*100, 2),
  check.names = FALSE
)

kable(reg_df, align="c",
      caption="Tabla 8. Betas CAPM — regresión OLS sobre S&P 500 (10 años diarios)") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  add_footnote(sprintf("Beta del portafolio ponderado: β_p = Σ(w_i × β_i) = %.4f", beta_port),
               notation="symbol") %>%
  row_spec(0, background="#003087", color="white")
Tabla 8. Betas CAPM — regresión OLS sobre S&P 500 (10 años diarios)
Acción Alpha Beta p-valor β Peso (%)
PG.(Intercept) PG 0.02688 0.4849 0.2159 0 0.00
SYK.(Intercept) SYK -0.00312 0.9701 0.4518 0 2.44
WM.(Intercept) WM 0.08367 0.5633 0.2749 0 97.56
* Beta del portafolio ponderado: β_p = Σ(w_i × β_i) = 0.5732
par(mfrow=c(1,3), mar=c(4,4,3,1))
colores_b <- c(PG="#003087", SYK="#E31837", WM="#00703C")
for (tk in TICKERS) {
  x <- as.numeric(ret_idx)*100
  y <- as.numeric(ret_acc[,tk])*100
  m <- lm(y~x)
  plot(x, y, pch=16, cex=0.3, col=adjustcolor(colores_b[tk],0.3),
       xlab="Retorno S&P 500 (%)", ylab=paste("Retorno",tk,"(%)"),
       main=sprintf("%s | β=%.3f | R²=%.3f", tk, betas[tk],
                    summary(m)$r.squared), cex.main=0.95)
  abline(m, col=colores_b[tk], lwd=2)
  abline(h=0, v=0, col="gray60", lty=2)
}

par(mfrow=c(1,1))

Beta del portafolio ponderado: \(\beta_p = \sum_i w_i \beta_i =\) 0.5732

Con β_p = 0.5732 < 1, el portafolio es más defensivo que el mercado: ante una caída del 10% en el S&P 500, el portafolio se esperaría que cayera aproximadamente 5.73%, reflejando principalmente el comportamiento de WM (peso ~97%, β≈0.56). Esta baja beta es fundamental para el diseño de la cobertura, ya que reduce el número de contratos necesarios comparado con un portafolio agresivo.


8 Instrumento de Cobertura — E-mini S&P 500

F0 <- precio_futuro_ini
QF <- MULTIPLICADOR
VP <- CAPITAL

contrato_df <- data.frame(
  Característica = c("Activo subyacente","Bolsa / Plataforma","Símbolo CME",
                     "Multiplicador","Precio inicial F₀ (30/04/2026)",
                     "Valor nocional por contrato",
                     "Vencimientos disponibles","Margen inicial por contrato",
                     "Margen de mantenimiento por contrato",
                     "Mecanismo de liquidación","Ajuste mark-to-market",
                     "Horas de negociación"),
  Valor = c(
    "Índice S&P 500","CME Group (Chicago Mercantile Exchange)","ES",
    paste0("USD ", QF, " por punto índice"),
    paste0(F0, " puntos"),
    dollar(F0 * QF),
    "Trimestral: Mar (H) / Jun (M) / Sep (U) / Dic (Z)",
    dollar(MARGEN_INI), dollar(MARGEN_MANT),
    "Entrega en efectivo (cash settlement)",
    "Diario en práctica; mensual en este ejercicio (académico)",
    "Domingo 17:00 – Viernes 16:00 (hora CT)"
  )
)

kable(contrato_df, align=c("l","r"),
      caption="Tabla 9. Parámetros del contrato E-mini S&P 500 — CME Group") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(0, background="#003087", color="white")
Tabla 9. Parámetros del contrato E-mini S&P 500 — CME Group
Característica Valor
Activo subyacente Índice S&P 500
Bolsa / Plataforma CME Group (Chicago Mercantile Exchange)
Símbolo CME ES
Multiplicador USD 50 por punto índice
Precio inicial F₀ (30/04/2026) 7168 puntos
Valor nocional por contrato $358,400
Vencimientos disponibles Trimestral: Mar (H) / Jun (M) / Sep (U) / Dic (Z)
Margen inicial por contrato $14,000
Margen de mantenimiento por contrato $12,700
Mecanismo de liquidación Entrega en efectivo (cash settlement)
Ajuste mark-to-market Diario en práctica; mensual en este ejercicio (académico)
Horas de negociación Domingo 17:00 – Viernes 16:00 (hora CT)

9 Número Óptimo de Contratos

\[N^* = \frac{\beta_p \times V_p}{F_0 \times Q_F}\]

Donde: \(\beta_p\) = beta del portafolio, \(V_p\) = valor del portafolio (USD 20M), \(F_0\) = precio inicial del futuro (puntos), \(Q_F\) = multiplicador del contrato (USD 50/punto)

N_star_exacto <- beta_port * VP / (F0 * QF)
N_star_round  <- round(N_star_exacto)
error_pct     <- abs(N_star_exacto - N_star_round)/N_star_exacto*100

cat(sprintf("β_portafolio                 = %.4f\n", beta_port))
## β_portafolio                 = 0.5732
cat(sprintf("V_p (capital)                = USD %s\n",    format(VP, big.mark=",")))
## V_p (capital)                = USD 2e+07
cat(sprintf("F₀ (precio futuro)           = %d puntos\n", F0))
## F₀ (precio futuro)           = 7168 puntos
cat(sprintf("Q_F (multiplicador)          = USD %d/punto\n", QF))
## Q_F (multiplicador)          = USD 50/punto
cat(sprintf("F₀ × Q_F (nocional/contrato) = USD %s\n",    format(F0*QF, big.mark=",")))
## F₀ × Q_F (nocional/contrato) = USD 358,400
cat(sprintf("─────────────────────────────────────────────\n"))
## ─────────────────────────────────────────────
cat(sprintf("N* exacto                    = %.4f contratos\n", N_star_exacto))
## N* exacto                    = 31.9859 contratos
cat(sprintf("N* redondeado                = %d contratos\n",   N_star_round))
## N* redondeado                = 32 contratos
cat(sprintf("Error de discretización      = %.2f%%\n",          error_pct))
## Error de discretización      = 0.04%
cat(sprintf("Nocional cubierto total      = USD %s\n",    format(N_star_round*F0*QF, big.mark=",")))
## Nocional cubierto total      = USD 11,468,800
cat(sprintf("Margen inicial total         = USD %s\n",    format(N_star_round*MARGEN_INI, big.mark=",")))
## Margen inicial total         = USD 448,000
cat(sprintf("Margen mantenimiento total   = USD %s\n",    format(N_star_round*MARGEN_MANT, big.mark=",")))
## Margen mantenimiento total   = USD 406,400

Justificación del redondeo: Se aplica round() (entero más cercano) para minimizar el error de cobertura absoluto. Redondear hacia arriba (ceiling) generaría over-hedging, dejando al portafolio con una ligera posición neta corta que puede revertir el riesgo si el mercado sube. Redondear hacia abajo (floor) generaría under-hedging, dejando exposición residual sin cubrir. El error de discretización es de 0.04%, considerado negligible para propósitos prácticos (Hull, 2022).


10 Posición en Futuros: Corta vs Larga

N_CONT <- N_star_round

pos_df <- data.frame(
  Aspecto = c(
    "Posición del portafolio","Riesgo que se desea cubrir",
    "Posición adoptada en futuros",
    "Si el mercado BAJA (escenario favorable a la cobertura)",
    "Si el mercado SUBE (costo de la cobertura)",
    "Cuándo se usaría posición LARGA en futuros",
    "Riesgo residual no cubierto"
  ),
  Descripción = c(
    "LARGA: portafolio de USD 20M en PG, SYK, WM (acciones en cartera)",
    "Caída sistemática del índice S&P 500 → pérdida proporcional al β_p en el valor de las acciones",
    paste0("CORTA en ", N_CONT, " contratos E-mini S&P 500 (posición vendedora de futuros)"),
    paste0("Ganancia en posición corta de futuros compensa la pérdida en acciones ✔ ",
           "(efecto neto: portafolio prácticamente neutral al movimiento del índice)"),
    paste0("Pérdida en posición corta reduce la ganancia en acciones ✗ ",
           "(es el costo explícito de la cobertura; el inversor renuncia al upside del mercado)"),
    "Cuando se anticipa COMPRA futura de acciones y se teme ALZA de precios antes de adquirirlas (cobertura anticipatoria)",
    "Riesgo idiosincrático (α_i) de cada acción; riesgo de base; riesgo de liquidez; cobertura imperfecta por redondeo"
  )
)

kable(pos_df, col.names=c("Aspecto","Descripción"),
      caption="Tabla 10. Análisis de la posición en futuros — justificación técnica") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(3, bold=TRUE, background="#fff3cd") %>%
  row_spec(4, background="#e8f5e9") %>%
  row_spec(5, background="#ffebee") %>%
  row_spec(0, background="#003087", color="white")
Tabla 10. Análisis de la posición en futuros — justificación técnica
Aspecto Descripción
Posición del portafolio LARGA: portafolio de USD 20M en PG, SYK, WM (acciones en cartera)
Riesgo que se desea cubrir Caída sistemática del índice S&P 500 → pérdida proporcional al β_p en el valor de las acciones
Posición adoptada en futuros CORTA en 32 contratos E-mini S&P 500 (posición vendedora de futuros)
Si el mercado BAJA (escenario favorable a la cobertura) Ganancia en posición corta de futuros compensa la pérdida en acciones ✔ (efecto neto: portafolio prácticamente neutral al movimiento del índice)
Si el mercado SUBE (costo de la cobertura) Pérdida en posición corta reduce la ganancia en acciones ✗ (es el costo explícito de la cobertura; el inversor renuncia al upside del mercado)
Cuándo se usaría posición LARGA en futuros Cuando se anticipa COMPRA futura de acciones y se teme ALZA de precios antes de adquirirlas (cobertura anticipatoria)
Riesgo residual no cubierto Riesgo idiosincrático (α_i) de cada acción; riesgo de base; riesgo de liquidez; cobertura imperfecta por redondeo

Análisis posición siempre-corta vs siempre-larga:

  • Siempre corta (nuestro caso): Protege contra caídas del mercado. En mercados alcistas, genera pérdidas en futuros que reducen la rentabilidad total del portafolio. Apropiado para inversores que priorizan la preservación del capital.

  • Siempre larga: Amplifica ganancias en mercados alcistas pero también amplifica pérdidas en bajistas. Solo tiene sentido si se anticipa una compra futura de acciones (eliminar riesgo de precio antes de ejecutar la compra).

Conclusión: Un inversionista que ya posee el portafolio y desea protegerse frente a una caída del mercado durante 4 años debe tomar posición CORTA. La estrategia no elimina el riesgo idiosincrático (riesgo propio de PG, SYK y WM), solo el riesgo sistemático medido por la beta.


11 Flujos Mensuales — Mark-to-Market

11.1 Simulación del precio del índice

mu_idx_d  <- as.numeric(mean(ret_idx))
sd_idx_d  <- as.numeric(sd(ret_idx))
S0_idx    <- as.numeric(tail(datos[,"GSPC"], 1))

set.seed(99)
precio_idx_mes <- numeric(N_MESES + 1)
precio_idx_mes[1] <- S0_idx
for (m in 2:(N_MESES+1)) {
  Z <- rnorm(N_MES)
  precio_idx_mes[m] <- precio_idx_mes[m-1] *
    exp(sum((mu_idx_d - 0.5*sd_idx_d^2) + sd_idx_d*Z))
}

T_trim <- 0.25
precio_fut_mes <- precio_idx_mes * exp(RF_ANUAL * T_trim)

# Mark-to-market mensual con margin call
saldo_margen <- MARGEN_INI * N_CONT
mtm_lst <- vector("list", N_MESES)

for (m in 1:N_MESES) {
  F_ini <- precio_fut_mes[m]
  F_fin <- precio_fut_mes[m+1]
  dF    <- F_fin - F_ini
  GP_l  <-  dF * QF * N_CONT   # posición larga
  GP_c  <- -GP_l                # posición corta
  saldo_margen <- saldo_margen + GP_c
  mc <- 0
  if (saldo_margen < MARGEN_MANT * N_CONT) {
    mc <- MARGEN_INI * N_CONT - saldo_margen
    saldo_margen <- MARGEN_INI * N_CONT
  }
  mtm_lst[[m]] <- data.frame(
    Mes=m, Trim=ceiling(m/3),
    F_ini=round(F_ini,2), F_fin=round(F_fin,2), dF=round(dF,2),
    GP_larga=round(GP_l), GP_corta=round(GP_c),
    Saldo_margen=round(saldo_margen), Margin_call=round(mc)
  )
}
mtm_tabla <- do.call(rbind, mtm_lst)

# Tabla interactiva primeros 24 meses
datatable(
  mtm_tabla[1:24, c("Mes","Trim","F_ini","F_fin","dF","GP_larga","GP_corta",
                     "Saldo_margen","Margin_call")],
  colnames = c("Mes","Trim","F Inicial","F Final","ΔF","G/P Larga (USD)",
               "G/P Corta (USD)","Saldo Margen (USD)","Margin Call (USD)"),
  caption = "Tabla 11. Flujos mensuales — primeros 24 meses (posición corta)",
  options = list(pageLength=12, scrollX=TRUE),
  rownames=FALSE
) %>%
  formatRound(columns=c("F_ini","F_fin","dF"), digits=2) %>%
  formatCurrency(columns=c("GP_larga","GP_corta","Saldo_margen","Margin_call"),
                 currency="$", digits=0)
p_mtm <- ggplot(mtm_tabla, aes(x=Mes)) +
  geom_col(aes(y=GP_corta/1e3, fill=GP_corta >= 0), alpha=0.75, width=0.8) +
  geom_line(aes(y=Saldo_margen/1e3), color="navy", linewidth=1.1) +
  geom_hline(yintercept=MARGEN_MANT*N_CONT/1e3, linetype="dashed",
             color="red", linewidth=0.9) +
  geom_hline(yintercept=MARGEN_INI*N_CONT/1e3, linetype="dotted",
             color="darkgreen", linewidth=0.8) +
  geom_point(data=mtm_tabla[mtm_tabla$Margin_call>0,],
             aes(x=Mes, y=Margin_call/1e3), color="red", size=4, shape=8) +
  scale_fill_manual(values=c("TRUE"="forestgreen","FALSE"="firebrick"),
                    labels=c("FALSE"="Pérdida (mercado sube)","TRUE"="Ganancia (mercado baja)"),
                    name="G/P posición corta") +
  scale_x_continuous(breaks=seq(0,N_MESES,6),
                     labels=paste0("M",seq(0,N_MESES,6))) +
  annotate("text", x=1, y=MARGEN_MANT*N_CONT/1e3*1.05,
           label="Margen mantenimiento", color="red", size=3, hjust=0) +
  annotate("text", x=1, y=MARGEN_INI*N_CONT/1e3*1.02,
           label="Margen inicial", color="darkgreen", size=3, hjust=0) +
  labs(title="Figura 5. Flujos mensuales — posición corta E-mini S&P 500 (4 años)",
       subtitle=sprintf("%d contratos | F₀=%d puntos | Nocional: %s",
                        N_CONT, F0, dollar(N_CONT*F0*QF)),
       x="Mes", y="Miles USD",
       caption="✱ = Margin Call  |  Línea azul: saldo de margen  |  Línea roja: margen de mantenimiento") +
  theme_minimal(base_size=12) +
  theme(legend.position="bottom", plot.title=element_text(face="bold"))

ggplotly(p_mtm)

Análisis del mark-to-market: La posición corta genera ganancias cuando el futuro cae (mercado bajista) y pérdidas cuando el futuro sube. El saldo de margen fluctúa en torno al margen inicial ($448,000). Cuando el saldo cae por debajo del margen de mantenimiento ($406,400), se produce un margin call que obliga a reponer el margen al nivel inicial. Los margin calls representan el costo de liquidez de la cobertura: el inversor debe tener disponibilidad de efectivo inmediata para no ser forzado a cerrar la posición en momentos de mercado adverso.


12 Estrategia de Roll-Over Trimestral

12.1 Fundamento del roll-over

¿Por qué roll-over? Los contratos E-mini S&P 500 tienen vencimientos trimestrales (Mar/Jun/Sep/Dic). Para mantener la cobertura durante 4 años continuos, es necesario cerrar la posición al vencimiento de cada contrato y abrir una nueva posición en el siguiente vencimiento disponible. Este proceso de renovación se denomina roll-over y genera costos implícitos asociados al diferencial bid-ask y al riesgo de base (Hull, 2022).

gp_roll <- data.frame()
for (q in 1:16) {
  mi <- (q-1)*3+1; mf <- q*3
  if (mf > N_MESES) break
  Fa   <- precio_fut_mes[mi]
  Fc   <- precio_fut_mes[mf+1]
  GPc  <- (Fa - Fc) * QF * N_CONT
  base <- precio_idx_mes[mf+1] - Fc
  gp_roll <- rbind(gp_roll, data.frame(
    Trimestre  = q,
    F_apertura = round(Fa,2),
    F_cierre   = round(Fc,2),
    Variación  = round(Fc-Fa,2),
    GP_corta   = round(GPc),
    Base_pts   = round(base,2),
    Efecto_pct = round(GPc/CAPITAL*100, 3),
    Posicion   = ifelse(GPc>0,"Ganancia ✔","Pérdida ✗")
  ))
}

kable(gp_roll,
      col.names=c("Trim.","F Apertura","F Cierre","Variación","G/P Corta (USD)",
                  "Base (pts)","Efecto (%)","Resultado"),
      align="c", format.args=list(big.mark=","),
      caption="Tabla 12. Roll-over trimestral — ganancias/pérdidas y riesgo de base") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(which(gp_roll$GP_corta > 0), background="#e8f5e9") %>%
  row_spec(which(gp_roll$GP_corta < 0), background="#ffebee") %>%
  row_spec(0, background="#003087", color="white")
Tabla 12. Roll-over trimestral — ganancias/pérdidas y riesgo de base
Trim. F Apertura F Cierre Variación G/P Corta (USD) Base (pts) Efecto (%) Resultado
1 7,214.16 6,097.52 -1,116.63 1,786,612 -66.10 8.933 Ganancia ✔
2 6,097.52 5,968.52 -129.00 206,402 -64.70 1.032 Ganancia ✔
3 5,968.52 6,560.09 591.56 -946,500 -71.12 -4.732 Pérdida ✗
4 6,560.09 6,113.57 -446.52 714,431 -66.28 3.572 Ganancia ✔
5 6,113.57 6,460.23 346.67 -554,668 -70.03 -2.773 Pérdida ✗
6 6,460.23 6,123.81 -336.42 538,272 -66.39 2.691 Ganancia ✔
7 6,123.81 6,329.75 205.93 -329,496 -68.62 -1.647 Pérdida ✗
8 6,329.75 6,888.56 558.81 -894,101 -74.68 -4.471 Pérdida ✗
9 6,888.56 6,235.83 -652.73 1,044,374 -67.60 5.222 Ganancia ✔
10 6,235.83 6,757.88 522.05 -835,278 -73.26 -4.176 Pérdida ✗
11 6,757.88 6,796.37 38.49 -61,591 -73.68 -0.308 Pérdida ✗
12 6,796.37 6,445.66 -350.71 561,136 -69.88 2.806 Ganancia ✔
13 6,445.66 7,565.57 1,119.91 -1,791,860 -82.02 -8.959 Pérdida ✗
14 7,565.57 7,833.21 267.63 -428,213 -84.92 -2.141 Pérdida ✗
15 7,833.21 8,744.68 911.47 -1,458,356 -94.80 -7.292 Pérdida ✗
16 8,744.68 8,445.63 -299.05 478,478 -91.56 2.392 Ganancia ✔
gp_roll$GP_acumulada <- cumsum(gp_roll$GP_corta)

p_roll <- ggplot(gp_roll, aes(x=Trimestre)) +
  geom_col(aes(y=GP_corta/1e6, fill=GP_corta>=0), alpha=0.8) +
  geom_line(aes(y=GP_acumulada/1e6), color="navy", linewidth=1.2) +
  geom_point(aes(y=GP_acumulada/1e6), color="navy", size=2.5) +
  geom_hline(yintercept=0, linetype="dashed", color="gray50") +
  scale_fill_manual(values=c("TRUE"="forestgreen","FALSE"="firebrick"),
                    labels=c("FALSE"="Pérdida","TRUE"="Ganancia")) +
  scale_x_continuous(breaks=1:16, labels=paste0("Q",1:16)) +
  labs(title="Figura 6. G/P por trimestre y resultado acumulado del roll-over",
       x="Trimestre", y="Millones USD",
       caption="Barras: G/P trimestral | Línea azul: G/P acumulada") +
  theme_minimal(base_size=12) +
  theme(legend.position="bottom", axis.text.x=element_text(angle=45, hjust=1))

ggplotly(p_roll)

Riesgo de base: La base = Precio spot – Precio futuro. Al vencimiento del contrato, la base converge a cero (convergencia del futuro hacia el spot). Sin embargo, al hacer roll-over antes del vencimiento, la base puede ser distinta de cero, generando una cobertura imperfecta. Los valores negativos de base en la tabla confirman que el futuro se negocia por encima del spot (contango), lo cual es el estado normal del mercado para futuros sobre índices con dividendos (Hull, 2022).

Estrategia siempre-corta vs siempre-larga en roll-over: - Siempre corta: Acumula ganancias en entornos bajistas pero pierde en bull markets. Durante el período analizado, el mercado es predominantemente alcista, lo que genera un resultado acumulado negativo para la posición corta en los últimos trimestres. - Siempre larga: Amplificaría las ganancias del portafolio en mercados alcistas pero eliminaría la cobertura bajista, exponiendo al inversor a caídas del mercado sin protección.


13 Valor Esperado de la Cobertura Trimestral

13.1 Tasa libre de riesgo y marco teórico

Tasa libre de riesgo utilizada: ^TNX (CBOE Interest Rate 10-Year Treasury Index) = 4.36% anual al 30/04/2026. Esta tasa fue seleccionada porque: (i) corresponde al benchmark estándar de libre de riesgo para el mercado estadounidense, (ii) es la tasa de referencia implícita en los contratos de futuros del CME (relación costo de acarreo), y (iii) es la tasa observable y verificable en la fecha inicial del ejercicio (Hull, 2022; Bodie et al., 2021).

Precio teórico del futuro (costo de acarreo): \(F_0 = S_0 \times e^{(r_f - d) \times T}\), donde \(d\) es el dividend yield del índice. Con dividendos incorporados en los precios ajustados, la aproximación usada es \(F_0 \approx S_0 \times e^{r_f \times T}\).

VE_sin_cob   <- CAPITAL * exp(mu_port * T_trim)
VE_fut_teo   <- F0 * exp(RF_ANUAL * T_trim)
VE_pos_corta <- (F0 - VE_fut_teo) * QF * N_CONT
VE_cubierto  <- VE_sin_cob + VE_pos_corta
costo_cob    <- VE_sin_cob - VE_cubierto

ve_df <- data.frame(
  Concepto = c(
    "Tasa libre de riesgo (^TNX, 30/04/2026)",
    "Horizonte de cálculo (T)",
    "VE portafolio SIN cobertura (1 trimestre)",
    "Precio teórico futuro F₀ × e^(rf×T)",
    "VE posición corta en futuros",
    "VE portafolio CUBIERTO (1 trimestre)",
    "Costo explícito de la cobertura"
  ),
  Valor = c(
    paste0(RF_ANUAL*100,"% anual"),
    paste0(T_trim," años (3 meses)"),
    dollar(round(VE_sin_cob)),
    round(VE_fut_teo, 2),
    dollar(round(VE_pos_corta)),
    dollar(round(VE_cubierto)),
    dollar(round(costo_cob))
  )
)

kable(ve_df, align=c("l","r"),
      caption="Tabla 13. Valor esperado de la cobertura trimestral") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(6, bold=TRUE, background="#eef4fb") %>%
  row_spec(7, bold=TRUE, background="#fff8e1") %>%
  row_spec(0, background="#003087", color="white")
Tabla 13. Valor esperado de la cobertura trimestral
Concepto Valor
Tasa libre de riesgo (^TNX, 30/04/2026) 4.36% anual
Horizonte de cálculo (T) 0.25 años (3 meses)
VE portafolio SIN cobertura (1 trimestre) $20,776,719
Precio teórico futuro F₀ × e^(rf×T) 7246.56
VE posición corta en futuros -$125,694
VE portafolio CUBIERTO (1 trimestre) $20,651,025
Costo explícito de la cobertura $125,694

Interpretación: El portafolio cubierto tiene un VE de $20,651,025, levemente inferior al portafolio sin cobertura ($20,776,719). La diferencia de $125,694 representa el costo de la cobertura: la prima que el inversor paga para reducir la exposición al riesgo sistémico. Este es el trade-off fundamental de la cobertura: se reduce riesgo a costa de reducir rentabilidad esperada. Sin embargo, este costo es conocido y acotado, mientras que la pérdida potencial sin cobertura puede ser mucho mayor (como ilustra el VaR al 99%).


14 Rendimiento Mensual y Valor de la Cartera

14.1 Dividendos y retorno total

Efecto de los dividendos sobre el retorno total:

Los tres componentes del portafolio pagan dividendos regulares:

Acción Yield Anual Pago típico Impacto en estrategia
PG ~2.4% anual Trimestral Al usar precios ajustados Yahoo Finance, los dividendos ya están incluidos en los retornos históricos calculados mediante \(r_t = \ln(P_t^{adj}/P_{t-1}^{adj})\). Por tanto, el retorno esperado de PG del 8.68% anual incluye tanto apreciación de capital como reinversión de dividendos.
SYK ~1.0% anual Trimestral Ídem. El retorno de 11.68% anual incorpora los dividendos reinvertidos.
WM ~1.5% anual Trimestral Ídem. El retorno de 15.33% anual ya incluye la contribución de dividendos.

Conclusión: Los dividendos están implícitamente incorporados en los retornos calculados. No se deben sumar por separado. Si se usaran precios sin ajustar, el retorno real del portafolio sería subestimado, pues omitiría entre 1.0% y 2.4% de retorno anual por acción (Bodie et al., 2021).

14.2 Evolución del valor de la cartera bajo tres escenarios

valor_sc  <- CAPITAL * exp(mu_port * (1:N_MESES)/12)
valor_cub <- numeric(N_MESES)
for (m in 1:N_MESES) {
  q  <- ceiling(m/3)
  aj <- if (q <= nrow(gp_roll)) gp_roll$GP_corta[q]/3 else 0
  valor_cub[m] <- valor_sc[m] + aj
}
valor_var <- CAPITAL * exp((mu_port - VaR_99_mc) * (1:N_MESES)/12)

df_ev <- data.frame(
  Mes = 1:N_MESES,
  `Sin cobertura`    = valor_sc/1e6,
  `Cubierto`         = valor_cub/1e6,
  `Escenario VaR 99%` = valor_var/1e6,
  check.names = FALSE
)
df_ev_long <- pivot_longer(df_ev, -Mes, names_to="Escenario", values_to="Valor_MM")

p_ev <- ggplot(df_ev_long, aes(x=Mes, y=Valor_MM, color=Escenario)) +
  geom_line(linewidth=1.2) +
  geom_hline(yintercept=CAPITAL/1e6, linetype="dashed", color="gray50") +
  geom_ribbon(data=df_ev_long[df_ev_long$Escenario %in% c("Sin cobertura","Escenario VaR 99%"),],
              aes(ymax=ifelse(Escenario=="Sin cobertura", Valor_MM, NA),
                  ymin=ifelse(Escenario=="Escenario VaR 99%", Valor_MM, NA)),
              fill="lightcoral", alpha=0.15, color=NA) +
  scale_color_manual(values=c("Sin cobertura"="steelblue",
                               "Cubierto"="forestgreen",
                               "Escenario VaR 99%"="firebrick")) +
  scale_x_continuous(breaks=seq(0,N_MESES,6)) +
  scale_y_continuous(labels=dollar_format(suffix=" M")) +
  annotate("text", x=2, y=CAPITAL/1e6+0.3, label="Capital inicial: USD 20M",
           color="gray50", size=3, hjust=0) +
  labs(title="Figura 7. Evolución del valor de la cartera — 4 años (48 meses)",
       subtitle="Comparación: portafolio libre vs cubierto vs escenario pesimista VaR 99%",
       x="Mes", y="Valor (millones USD)", color="Escenario") +
  theme_minimal(base_size=12) +
  theme(legend.position="bottom", plot.title=element_text(face="bold"))

ggplotly(p_ev)
meses_clave <- c(6, 12, 24, 36, 48)
res_df <- data.frame(
  Escenario = c("Sin cobertura","Cubierto","VaR 99% (pesimista)"),
  rbind(
    dollar(round(c(valor_sc[meses_clave]))),
    dollar(round(c(valor_cub[meses_clave]))),
    dollar(round(c(valor_var[meses_clave])))
  )
)
colnames(res_df) <- c("Escenario", paste0("Mes ", meses_clave))

kable(res_df, align=c("l",rep("r",5)),
      caption="Tabla 14. Valor esperado de la cartera por escenario y horizonte") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(0, background="#003087", color="white")
Tabla 14. Valor esperado de la cartera por escenario y horizonte
Escenario Mes 6 Mes 12 Mes 24 Mes 36 Mes 48
Sin cobertura $21,583,602 $23,292,594 $27,127,247 $31,593,198 $36,794,377
Cubierto $21,652,403 $23,530,738 $26,829,214 $31,780,243 $36,953,870
VaR 99% (pesimista) $20,322,225 $20,649,642 $21,320,386 $22,012,917 $22,727,943

Interpretación del escenario pesimista VaR 99%: El escenario ajustado por VaR 99% representa la trayectoria del portafolio si el retorno mensual realizado fuera consistentemente igual al VaR mensual negativo calculado. Al mes 48, incluso en este escenario extremo, el portafolio conserva un valor de $22,727,943 sobre los USD 20M iniciales, lo que confirma la robustez del portafolio defensivo para horizontes de largo plazo.


15 Sensibilidad de la Cobertura: β = 0.5 y β = 2

15.1 Análisis comparativo

betas_hip  <- c(0.5, beta_port, 2.0)
etiquetas  <- c("β = 0.5 (defensivo)","β actual del portafolio","β = 2.0 (agresivo)")

sens_df <- data.frame(
  Escenario            = etiquetas,
  Beta                 = betas_hip,
  `N* exacto`          = round(betas_hip * VP / (F0*QF), 4),
  `N* contratos`       = round(betas_hip * VP / (F0*QF)),
  `Nocional cubierto`  = dollar(round(round(betas_hip*VP/(F0*QF)) * F0*QF)),
  `Exp. sistemática`   = dollar(round(betas_hip * VP)),
  `Costo margen ini.`  = dollar(round(round(betas_hip*VP/(F0*QF)) * MARGEN_INI)),
  check.names          = FALSE
)

kable(sens_df, align=c("l","c","c","c","r","r","r"),
      caption="Tabla 15. Sensibilidad del número de contratos según beta hipotético") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(2, bold=TRUE, background="#eef4fb") %>%
  row_spec(3, background="#fff3cd") %>%
  row_spec(0, background="#003087", color="white")
Tabla 15. Sensibilidad del número de contratos según beta hipotético
Escenario Beta N* exacto N* contratos Nocional cubierto Exp. sistemática Costo margen ini.
β = 0.5 (defensivo) 0.5000000 27.9018 28 $10,035,200 $10,000,000 $392,000
β actual del portafolio 0.5731877 31.9859 32 $11,468,800 $11,463,753 $448,000
β = 2.0 (agresivo) 2.0000000 111.6071 112 $40,140,800 $40,000,000 $1,568,000
beta_rango <- seq(0.3, 2.5, by=0.05)
n_rango    <- beta_rango * VP / (F0*QF)

p_sens <- ggplot(data.frame(beta=beta_rango, N=n_rango), aes(x=beta, y=N)) +
  geom_line(color="steelblue", linewidth=1.3) +
  geom_ribbon(aes(ymin=0, ymax=N), fill="steelblue", alpha=0.1) +
  geom_vline(xintercept=1, linetype="dashed", color="gray50") +
  annotate("text", x=1.02, y=max(n_rango)*0.8, label="β = 1\n(= mercado)",
           color="gray50", size=3, hjust=0) +
  geom_point(aes(x=0.5,       y=round(0.5*VP/(F0*QF))),
             color="forestgreen", size=5, shape=19) +
  geom_point(aes(x=beta_port, y=N_CONT),
             color="red", size=5, shape=19) +
  geom_point(aes(x=2.0,       y=round(2.0*VP/(F0*QF))),
             color="orange", size=5, shape=19) +
  annotate("label", x=0.5,       y=round(0.5*VP/(F0*QF))+5,
           label=sprintf("β=0.5\n%d contratos", round(0.5*VP/(F0*QF))),
           color="forestgreen", size=3, fill="white") +
  annotate("label", x=beta_port, y=N_CONT+5,
           label=sprintf("β=%.2f (actual)\n%d contratos", beta_port, N_CONT),
           color="red", size=3, fill="white") +
  annotate("label", x=2.0,       y=round(2.0*VP/(F0*QF))+5,
           label=sprintf("β=2.0\n%d contratos", round(2.0*VP/(F0*QF))),
           color="darkorange", size=3, fill="white") +
  labs(title="Figura 8. N* de contratos en función de la beta del portafolio",
       subtitle=sprintf("N* = β × Vp / (F₀ × Q_F) | Vp=USD %sM | F₀=%d pts | Q_F=%d",
                        CAPITAL/1e6, F0, QF),
       x="Beta del portafolio (β)", y="N* contratos óptimos") +
  theme_minimal(base_size=12) +
  theme(plot.title=element_text(face="bold"))

ggplotly(p_sens)

¿Por qué un β mayor requiere más contratos? La beta cuantifica la sensibilidad del portafolio a movimientos del índice. Un portafolio con β=2 amplifica el movimiento del mercado el doble: si el S&P 500 cae 10%, el portafolio pierde ~20%. Para neutralizar esa mayor exposición, se necesitan más contratos cortos. La relación es exactamente lineal: duplicar la beta duplica el número de contratos necesarios. Un portafolio con β=0.5 ya tiene inherentemente menor exposición al mercado, por lo que requiere menos cobertura externa (Hull, 2022).


16 Simulación GBM — Trayectorias de Precios

colores <- c(PG="#003087", SYK="#E31837", WM="#00703C")
n_show  <- 100

par(mfrow=c(1,3), mar=c(4,4,3,1), bg="white")
for (tk in TICKERS) {
  paths <- sim_precios[[tk]]
  med   <- apply(paths, 2, median)
  p05   <- apply(paths, 2, quantile, 0.05)
  p95   <- apply(paths, 2, quantile, 0.95)
  p25   <- apply(paths, 2, quantile, 0.25)
  p75   <- apply(paths, 2, quantile, 0.75)
  ylim  <- range(p05, p95)

  plot(0:N_MES, paths[1,], type="l",
       col=adjustcolor(colores[tk], 0.06),
       ylim=ylim, xlab="Días hábiles", ylab="Precio (USD)",
       main=sprintf("GBM — %s\nμ=%.1f%% | σ=%.1f%% (anual)",
                    tk, mu_anual[tk]*100, sd_anual[tk]*100),
       cex.main=0.95, font.main=2)
  for (i in 2:n_show)
    lines(0:N_MES, paths[i,], col=adjustcolor(colores[tk], 0.06))
  polygon(c(0:N_MES, rev(0:N_MES)), c(p05, rev(p95)),
          col=adjustcolor(colores[tk], 0.12), border=NA)
  polygon(c(0:N_MES, rev(0:N_MES)), c(p25, rev(p75)),
          col=adjustcolor(colores[tk], 0.20), border=NA)
  lines(0:N_MES, med, col=colores[tk], lwd=2.5)
  lines(0:N_MES, p05, col="gray30", lwd=1, lty=2)
  lines(0:N_MES, p95, col="gray30", lwd=1, lty=2)
  abline(h=S0[tk], col="gray50", lty=3, lwd=1)
  legend("topleft",
         legend=c("Mediana","IC 50%","IC 90%","Precio inicial"),
         lty=c(1,NA,2,3), lwd=c(2.5,NA,1,1),
         pch=c(NA,15,NA,NA),
         col=c(colores[tk], adjustcolor(colores[tk],0.4), "gray30","gray50"),
         bty="n", cex=0.75)
}

par(mfrow=c(1,1))

17 Resumen Ejecutivo

resumen_df <- data.frame(
  Indicador = c(
    "Capital inicial","Acciones seleccionadas","Horizonte de inversión",
    "Retorno esperado portafolio","Volatilidad portafolio","Sharpe Ratio",
    "Beta portafolio ponderado",
    "VaR mensual 99% (Monte Carlo GBM)","VaR mensual 95% (Monte Carlo GBM)",
    "Contratos E-mini (posición CORTA)","Nocional cubierto",
    "Margen inicial total requerido",
    "Tasa libre de riesgo (^TNX)","VE portafolio cubierto (1 trim.)",
    "Valor esperado a 4 años (sin cob.)","Valor esperado a 4 años (cubierto)"
  ),
  Valor = c(
    dollar(CAPITAL), paste(TICKERS, collapse=" | "),
    paste(HORIZONTE, "años"),
    paste0(round(mu_port*100,2), "% anual"),
    paste0(round(sigma_port*100,2), "% anual"),
    round(sharpe_opt, 4),
    round(beta_port, 4),
    paste0(round(VaR_99_mc*100,2), "% = ", dollar(round(VaR_99_mc*CAPITAL))),
    paste0(round(VaR_95_mc*100,2), "% = ", dollar(round(VaR_95_mc*CAPITAL))),
    paste0(N_CONT, " contratos"),
    dollar(N_CONT*F0*QF),
    dollar(N_CONT*MARGEN_INI),
    paste0(RF_ANUAL*100, "% anual (^TNX)"),
    dollar(round(VE_cubierto)),
    dollar(round(valor_sc[48])),
    dollar(round(valor_cub[48]))
  )
)

kable(resumen_df, align=c("l","r"),
      caption="Tabla 16. Resumen ejecutivo del ejercicio") %>%
  kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=FALSE) %>%
  row_spec(c(4,5,6,7,8,9,10,11,12), bold=TRUE) %>%
  row_spec(0, background="#003087", color="white") %>%
  pack_rows("Portafolio", 1, 7) %>%
  pack_rows("Riesgo (VaR)", 8, 9) %>%
  pack_rows("Cobertura con futuros", 10, 16)
Tabla 16. Resumen ejecutivo del ejercicio
Indicador Valor
Portafolio
Capital inicial $20,000,000
Acciones seleccionadas PG &#124; SYK &#124; WM
Horizonte de inversión 4 años
Retorno esperado portafolio 15.24% anual
Volatilidad portafolio 19.3% anual
Sharpe Ratio 0.5639
Beta portafolio ponderado 0.5732
Riesgo (VaR)
VaR mensual 99% (Monte Carlo GBM) 12.04% = $2,408,749
VaR mensual 95% (Monte Carlo GBM) 8.03% = $1,605,621
Cobertura con futuros
Contratos E-mini (posición CORTA) 32 contratos
Nocional cubierto $11,468,800
Margen inicial total requerido $448,000
Tasa libre de riesgo (^TNX) 4.36% anual (^TNX)
VE portafolio cubierto (1 trim.) $20,651,025
Valor esperado a 4 años (sin cob.) $36,794,377
Valor esperado a 4 años (cubierto) $36,953,870

18 Referencias

Referencias en normas APA 7ª edición:

Bodie, Z., Kane, A., & Marcus, A. J. (2021). Investments (12th ed.). McGraw-Hill Education.

CME Group. (2026). E-mini S&P 500 futures contract specifications. https://www.cmegroup.com/trading/equity-index/us-index/e-mini-sandp500.html

Elton, E. J., Gruber, M. J., Brown, S. J., & Goetzmann, W. N. (2014). Modern portfolio theory and investment analysis (9th ed.). John Wiley & Sons.

Hull, J. C. (2022). Options, futures, and other derivatives (11th ed.). Pearson Education.

Keitt, T. (2023). quantmod: Quantitative financial modelling framework (R package version 0.4.22). CRAN. https://CRAN.R-project.org/package=quantmod

Markowitz, H. (1952). Portfolio selection. The Journal of Finance, 7(1), 77–91. https://doi.org/10.2307/2975974

Sharpe, W. F. (1964). Capital asset prices: A theory of market equilibrium under conditions of risk. The Journal of Finance, 19(3), 425–442. https://doi.org/10.2307/2977928

Yahoo Finance. (2026). Historical market data [Dataset]. https://finance.yahoo.com


Informe generado en R Markdown v2.27 | R v4.3+ | Datos: Yahoo Finance API | Futuros: CME Group

Publicado en RPubs. Para reproducir: ejecutar el código fuente en RStudio con los paquetes indicados.