Resumen ejecutivo

Objetivo. Construir un portafolio óptimo media-varianza con tres acciones del S&P 500 y diseñar una estrategia de cobertura mediante futuros E-mini S&P 500 (CME), evaluando llamados al margen, roll-over trimestral y sensibilidades por beta.

Parámetros. Capital inicial USD 20.000.000 · Inicio 30-abr-2026 · Horizonte 4 años · Histórico 10 años (30-abr-2016 a 30-abr-2026, mensual).

Acciones seleccionadas. Cummins (CMI), Hershey (HSY), Cadence Design Systems (CDNS) — tres sectores no correlacionados.


1 Selección y análisis fundamental

1.1 Acciones seleccionadas

Acciones seleccionadas para el portafolio
Ticker Empresa Sector Subsector Razón de inclusión
CMI Cummins Inc.  Industriales Motores diésel y energía limpia Exposición cíclica industrial + transición energética (hidrógeno)
HSY The Hershey Company Consumo Básico Confitería y snacks Defensiva — baja correlación con ciclo económico
CDNS Cadence Design Systems Tecnología Software EDA para semiconductores Crecimiento estructural del semiconductor (sin ser mega-cap obvia)

1.2 Cummins Inc. (CMI)

Descripción general. Cummins Inc. es una corporación industrial estadounidense fundada en 1919 con sede en Columbus, Indiana. Diseña, fabrica, distribuye y mantiene motores de combustión interna (principalmente diésel y gas natural), sistemas de filtración, sistemas eléctricos de potencia y, más recientemente, soluciones de energía baja en carbono mediante su unidad Accelera by Cummins (electrólisis, celdas de combustible de hidrógeno, baterías y trenes motrices eléctricos).

Sector económico y subsegmento. Industriales / Maquinaria — Construcción, Minería y Vehículos Comerciales (GICS 20106020). Es la principal proveedora global de motores para camiones medianos y pesados (Class 8) en Norteamérica.

Principales fuentes de ingresos. Cummins reporta cinco segmentos: (1) Engine — motores para camiones medianos, pesados, y aplicaciones industriales; (2) Distribution — repuestos y servicio postventa a través de su red global; (3) Components — sistemas de filtración, postratamiento de emisiones, turbocargadores; (4) Power Systems — generadores de energía estacionaria y motores industriales de gran tamaño; (5) Accelera — tecnologías de cero emisiones (en fase de inversión, aún no rentable). Los ingresos de 2024 superaron los USD 34.000 millones, con Engine y Distribution aportando el mayor margen operativo.

Evolución reciente del precio. Tras el desplome de 2022 (ciclo de subida de tasas + temores recesivos), CMI inició una recuperación sostenida en 2023–2025, impulsada por (i) la fortaleza del ciclo de reemplazo de flotas Class 8 en EE.UU.; (ii) el cumplimiento normativo EPA 2027 que adelantará pre-buy de motores en 2026; (iii) el desempeño favorable de Power Systems gracias a la demanda de generación distribuida para data centers de IA. La acción ha mostrado mayor sensibilidad al ciclo industrial y a noticias macroeconómicas (PMI manufacturero, tasas de interés).

Situación financiera. Cummins mantiene grado de inversión (Moody’s A2, S&P A) con flujo de caja libre robusto (>USD 2.000 M en años normales), margen operativo en torno al 12–14%, ROE histórico cercano al 25% y un programa de dividendo creciente con más de 15 años de aumentos consecutivos (yield ~2.5%). La deuda neta es manejable y la compañía continúa recomprando acciones.

Expectativas a 12 meses. El consenso de analistas (Refinitiv, Yahoo Finance) sitúa el precio objetivo medio con upside moderado; los catalizadores principales son el pre-buy regulatorio EPA 2027, la rentabilización progresiva de Accelera, y el ciclo de capex de generación eléctrica. Los riesgos incluyen una recesión que retrase el reemplazo de flotas y la presión de costos de la transición energética.

Justificación de inclusión. CMI aporta al portafolio (i) exposición cíclica industrial (β > 1 esperada), (ii) opcionalidad sobre la transición energética vía Accelera, (iii) flujo de caja y dividendo estable que amortigua la volatilidad, y (iv) baja correlación esperada con HSY (consumo defensivo) y CDNS (tecnología). Es la pata “valor cíclico” del portafolio.

1.3 The Hershey Company (HSY)

Descripción general. The Hershey Company es la principal compañía de confitería y snacks salados de Estados Unidos, fundada en 1894 por Milton S. Hershey y con sede en Hershey, Pensilvania. Es propietaria de marcas icónicas como Hershey’s, Reese’s, Kit Kat (licencia en EE.UU.), Twizzlers, Jolly Rancher, Ice Breakers, así como las marcas de snacks salados SkinnyPop y Dot’s Pretzels, adquiridas en años recientes para diversificar su portafolio.

Sector económico y subsegmento. Consumo Básico / Alimentos Empacados (GICS 30202030). Es uno de los componentes históricamente más defensivos del S&P 500, junto con Procter & Gamble, Coca-Cola y Pepsico.

Principales fuentes de ingresos. (1) North America Confectionery (>80% de los ingresos) — chocolates, gomas y dulces; (2) North America Salty Snacks — pretzels y popcorn premium; (3) International — operaciones en México, Brasil, India y otros mercados emergentes. La concentración geográfica en Norteamérica es alta (~90%), lo que limita la exposición cambiaria pero también restringe vías de crecimiento.

Evolución reciente del precio. Tras un período de bajo desempeño relativo en 2023–2024, marcado por la escalada histórica del precio del cacao (que se triplicó por las malas cosechas en África Occidental), HSY ha mostrado señales de estabilización en 2025–2026 a medida que: (i) la compañía implementa subidas de precios y reducción de tamaño de paquetes (shrinkflation); (ii) los precios del cacao se moderan parcialmente con mejores cosechas; (iii) la demanda en confitería se mantiene resiliente. La acción cotiza con un descuento histórico frente a su múltiplo P/E mediano de los últimos 10 años.

Situación financiera. Hershey mantiene calificación A1 / A (Moody’s / S&P) con margen bruto histórico cercano al 45% (presionado al ~42% por costos del cacao), margen operativo de ~22%, ROE superior al 50% (apalancado pero estable) y más de 95 años pagando dividendos ininterrumpidos, con un yield actual de ~2.7%. La generación de caja libre supera los USD 1.500 M anuales.

Expectativas a 12 meses. El consenso espera una recuperación gradual de márgenes a medida que se normalizan los costos del cacao en 2026–2027 y la compañía termine de implementar el traslado de costos. Catalizadores: éxito de las marcas de snacks adquiridos, expansión internacional, recuperación de márgenes. Riesgos: persistencia de costos altos del cacao, competencia de marcas privadas (private-label), tendencias de consumo hacia productos saludables.

Justificación de inclusión. HSY aporta al portafolio (i) carácter defensivo con β < 1 históricamente, (ii) dividendo estable y creciente que añade ~2.7% de retorno anual; (iii) baja correlación con CMI (cíclico) y muy baja correlación con CDNS (tecnología), maximizando el beneficio de diversificación; (iv) valuación atractiva tras la corrección por costos del cacao. Es la pata “defensiva con yield” del portafolio.

1.4 Cadence Design Systems (CDNS)

Descripción general. Cadence Design Systems Inc. es una compañía global de software y servicios de automatización de diseño electrónico (EDA) fundada en 1988 con sede en San José, California. Su software es esencial para el diseño, verificación y fabricación de semiconductores, sistemas integrados y, más recientemente, para la simulación y verificación basada en IA. Sus principales clientes son los fabricantes de chips (TSMC, Intel, Samsung, AMD, NVIDIA, Qualcomm, Apple) y empresas de hardware que diseñan sus propios chips (incluyendo hyperscalers como Google, Microsoft, Amazon).

Sector económico y subsegmento. Tecnología de la Información / Software de Aplicaciones (GICS 45103010). Es uno de los tres principales jugadores del oligopolio EDA junto con Synopsys y Siemens EDA (Mentor Graphics).

Principales fuentes de ingresos. Cadence reporta cinco categorías de productos: (1) Custom IC Design & Simulation — herramientas para circuitos analógicos/mixtos; (2) Digital IC Design & Signoff — flujo de diseño digital de avanzada (líder con sus tools Innovus y Tempus); (3) Functional Verification — emulación, simulación y prototipado FPGA, incluyendo la familia Palladium/Protium (>30% de los ingresos y mayor crecimiento); (4) IP (Intellectual Property) — bloques pre-diseñados (procesadores Tensilica, controladores DDR, PCIe, USB); (5) System Design & Analysis — software multi-físico (térmico, electromagnético, fluidos), donde Cadence ha invertido fuertemente en CFD y AI-driven simulation.

Evolución reciente del precio. CDNS ha sido una de las acciones más destacadas del S&P 500 en los últimos 5 años, impulsada por: (i) el boom estructural del diseño de chips de IA, donde el costo y complejidad del tape-out de un chip de 3 nm supera USD 500 M, requiriendo herramientas EDA cada vez más sofisticadas; (ii) el modelo de suscripción recurrente que ya representa >85% de los ingresos; (iii) acuerdos plurianuales con hyperscalers para el desarrollo de chips de IA personalizados; (iv) la integración de IA generativa en sus propias herramientas (Cadence Cerebrus, JedAI). Posibles correcciones recientes obedecen a (a) ajustes de valuación tras múltiples expansiones, (b) restricciones de exportación a China.

Situación financiera. Cadence presenta crecimiento de ingresos consistentemente >10% anual, margen operativo non-GAAP cercano al 42–45%, caja neta positiva (más caja que deuda) y un programa de recompra de acciones. No paga dividendos: prioriza la reinversión en R&D (~25% de los ingresos) y adquisiciones estratégicas. ROE histórico >25% (sin apalancamiento financiero significativo).

Expectativas a 12 meses. El consenso de analistas mantiene precios objetivo alcistas, sustentados en (i) la guía de la propia compañía con crecimiento de ingresos de doble dígito; (ii) el backlog que se mantiene en máximos históricos; (iii) el ramp-up de Palladium Z3/Z2 y nuevas herramientas con IA. Riesgos: ralentización del capex de semiconductores, mayor escrutinio antimonopolio, restricciones de exportación a China (~12% de los ingresos).

Justificación de inclusión. CDNS aporta al portafolio (i) exposición a crecimiento secular del semiconductor y de la IA, sin la concentración de riesgo idiosincrático de NVIDIA o las megacaps; (ii) β moderada-alta que aumenta el retorno esperado del portafolio (a costo de mayor sensibilidad al mercado, lo que justifica la cobertura con futuros); (iii) complementariedad con CMI (cíclico industrial) y HSY (defensivo); (iv) calidad financiera (caja neta positiva, márgenes elevados, modelo recurrente). Es la pata “crecimiento secular” del portafolio.

Recordatorio del enunciado: “El análisis debe conectar la información fundamental con la decisión de inversión, evitando una simple descripción de la empresa.”


2 Análisis estadístico de retornos

2.1 Descarga de datos

Frecuencia: precios mensuales ajustados (cierre ajustado por dividendos y splits). Ventana: 30-abr-2016 a 30-abr-2026 (10 años, dentro del rango 6–10 años permitido). Fuente: Yahoo Finance vía quantmod::getSymbols.

tickers     <- c("CMI", "HSY", "CDNS")
benchmark   <- "^GSPC"   # S&P 500 (mercado para CAPM)
rf_proxy    <- "^FVX"    # CBOE 5Y Treasury Yield (rango 3-5y, según enunciado)

start_date <- as.Date("2016-04-30")
end_date   <- as.Date("2026-04-30")

get_adj <- function(sym) {
  x <- quantmod::getSymbols(sym, from = start_date, to = end_date,
                            auto.assign = FALSE, src = "yahoo")
  Ad(x)
}

prices_daily <- do.call(merge, lapply(c(tickers, benchmark), get_adj))
colnames(prices_daily) <- c(tickers, "SPX")

# Convertir a fin de mes (último precio del mes)
prices_monthly <- apply.monthly(prices_daily, last)
prices_monthly <- na.omit(prices_monthly)

2.2 Evolución de precios (base 100)

prices_norm <- sweep(prices_monthly, 2, as.numeric(prices_monthly[1, ]), "/") * 100
df_long <- data.frame(
  Fecha = as.Date(index(prices_norm)),
  coredata(prices_norm)
) |>
  pivot_longer(-Fecha, names_to = "Activo", values_to = "Indice")

p <- ggplot(df_long, aes(x = Fecha, y = Indice, color = Activo)) +
  geom_line(linewidth = 0.7) +
  scale_y_continuous(labels = comma) +
  labs(title = "Evolución normalizada (Base 100 = abr-2016)",
       x = NULL, y = "Índice", color = NULL) +
  theme_minimal(base_size = 12)

plotly::ggplotly(p)

2.3 Retornos periódicos y anualización

Se utilizan retornos logarítmicos mensuales \(r_t = \ln(P_t / P_{t-1})\).

Anualización: \[ \mu_{anual} = 12 \cdot \mu_{mensual} \qquad \sigma_{anual} = \sqrt{12} \cdot \sigma_{mensual} \]

returns_m <- na.omit(Return.calculate(prices_monthly, method = "log"))

mu_m <- colMeans(returns_m[, tickers])
sd_m <- apply(returns_m[, tickers], 2, sd)
mu_a <- mu_m * 12
sd_a <- sd_m * sqrt(12)

stats <- data.frame(
  Acción              = tickers,
  `Retorno mensual`   = scales::percent(mu_m, accuracy = 0.01),
  `Retorno anual`     = scales::percent(mu_a, accuracy = 0.01),
  `Volatilidad mensual` = scales::percent(sd_m, accuracy = 0.01),
  `Volatilidad anual` = scales::percent(sd_a, accuracy = 0.01),
  check.names = FALSE
)
ktab(stats, "Estadísticas descriptivas (10 años de retornos mensuales)")
Estadísticas descriptivas (10 años de retornos mensuales)
Acción Retorno mensual Retorno anual Volatilidad mensual Volatilidad anual
CMI CMI 1.66% 19.91% 7.37% 25.52%
HSY HSY 0.79% 9.52% 6.07% 21.04%
CDNS CDNS 2.18% 26.13% 7.83% 27.11%

2.4 Matriz de varianzas-covarianzas y correlaciones

Sigma_m <- cov(returns_m[, tickers])
Sigma_a <- Sigma_m * 12
Corr    <- cor(returns_m[, tickers])

ktab(round(Sigma_a, 6), "Matriz de varianzas-covarianzas anualizada")
Matriz de varianzas-covarianzas anualizada
CMI HSY CDNS
CMI 0.065139 0.00538 0.018261
HSY 0.005380 0.04425 0.002580
CDNS 0.018261 0.00258 0.073517
ktab(round(Corr, 3),    "Matriz de correlaciones")
Matriz de correlaciones
CMI HSY CDNS
CMI 1.000 0.100 0.264
HSY 0.100 1.000 0.045
CDNS 0.264 0.045 1.000
corrplot(Corr, method = "color", addCoef.col = "black", tl.col = "black",
         number.cex = 1.1, type = "upper", diag = FALSE,
         col = colorRampPalette(c("#d73027", "white", "#1a9850"))(200))


3 Portafolio óptimo de media-varianza

Criterio: maximización del Sharpe Ratio (portafolio de tangencia), sin venta en corto y plenamente invertido (\(\sum w_i = 1, \; w_i \ge 0\)).

# Tasa libre de riesgo (placeholder; se refina en sección 10 con ^FVX en la fecha inicial)
rf_a <- 0.045

mu_vec <- as.numeric(mu_a); names(mu_vec) <- tickers
n      <- length(mu_vec)
ones   <- rep(1, n)
excess <- mu_vec - rf_a

# Solución analítica (permite cortos)
Sigma_inv <- solve(Sigma_a)
w_uncon   <- as.numeric(Sigma_inv %*% excess)
w_uncon   <- w_uncon / sum(w_uncon)

# Si los pesos analíticos tienen alguno negativo, resolver QP con restricción w >= 0
# Reformulación clásica para max Sharpe sin cortos:
#   min y' Σ y  s.a.  (μ - rf)' y = 1, y >= 0; luego w = y / 1' y
if (any(w_uncon < 0)) {
  Dmat <- 2 * Sigma_a
  dvec <- rep(0, n)
  Amat <- cbind(excess, diag(n))
  bvec <- c(1, rep(0, n))
  sol  <- quadprog::solve.QP(Dmat, dvec, Amat, bvec, meq = 1)
  y_opt <- sol$solution
  w_opt <- y_opt / sum(y_opt)
  metodo <- "QP con no-shorting"
} else {
  w_opt  <- w_uncon
  metodo <- "Solución analítica (todos los pesos positivos)"
}
names(w_opt) <- tickers

# Métricas del portafolio óptimo
capital <- 20e6
ret_p   <- as.numeric(t(w_opt) %*% mu_vec)
sd_p    <- as.numeric(sqrt(t(w_opt) %*% Sigma_a %*% w_opt))
sharpe  <- (ret_p - rf_a) / sd_p
montos  <- w_opt * capital

resumen_pf <- data.frame(
  Acción = tickers,
  Peso   = scales::percent(w_opt, accuracy = 0.01),
  Monto  = scales::dollar(montos, accuracy = 1)
)
ktab(resumen_pf, sprintf("Portafolio óptimo (tangencia) — método: %s", metodo))
Portafolio óptimo (tangencia) — método: Solución analítica (todos los pesos positivos)
Acción Peso Monto
CMI CMI 32.50% $6,500,959
HSY HSY 16.16% $3,231,767
CDNS CDNS 51.34% $10,267,274
resumen_metr <- data.frame(
  Métrica = c("Retorno esperado anual", "Volatilidad anual", "Sharpe Ratio (rf = 4.5%)"),
  Valor   = c(scales::percent(ret_p, 0.01),
              scales::percent(sd_p,  0.01),
              round(sharpe, 3))
)
ktab(resumen_metr, "Métricas del portafolio óptimo")
Métricas del portafolio óptimo
Métrica Valor
Retorno esperado anual 21.42%
Volatilidad anual 18.57%
Sharpe Ratio (rf = 4.5%) 0.911

3.1 Frontera eficiente (visualización)

# Frontera eficiente: target returns vs vol
target_rets <- seq(min(mu_vec), max(mu_vec), length.out = 50)
front <- map_dfr(target_rets, function(tr) {
  Dmat <- 2 * Sigma_a
  dvec <- rep(0, n)
  # Restricciones: 1'w = 1, μ'w = tr, w >= 0
  Amat <- cbind(rep(1, n), mu_vec, diag(n))
  bvec <- c(1, tr, rep(0, n))
  sol <- tryCatch(quadprog::solve.QP(Dmat, dvec, Amat, bvec, meq = 2),
                  error = function(e) NULL)
  if (is.null(sol)) return(NULL)
  w <- sol$solution
  data.frame(target = tr, vol = sqrt(t(w) %*% Sigma_a %*% w)[1, 1])
})

p_front <- ggplot(front, aes(x = vol, y = target)) +
  geom_line(linewidth = 1, color = "#1f77b4") +
  geom_point(data = data.frame(vol = sd_p, target = ret_p),
             aes(x = vol, y = target), color = "red", size = 4) +
  geom_text(data = data.frame(vol = sd_p, target = ret_p, lab = "Tangencia"),
            aes(label = lab), nudge_y = 0.01, color = "red") +
  scale_x_continuous(labels = percent) +
  scale_y_continuous(labels = percent) +
  labs(x = "Volatilidad anual", y = "Retorno esperado anual",
       title = "Frontera eficiente (sin venta en corto)") +
  theme_minimal(base_size = 12)

plotly::ggplotly(p_front)

4 Valor en Riesgo (VaR)

VaR mensual del portafolio bajo dos enfoques: paramétrico (asumiendo normalidad) e histórico (sin asumir distribución).

Fórmulas:

  • VaR paramétrico (normal): \[ \text{VaR}_{\alpha} = -\left( \mu_p - z_{\alpha} \cdot \sigma_p \right) \cdot V_p \] donde \(z_{0.01} = 2.326\), \(z_{0.05} = 1.645\).

  • VaR histórico: \(\alpha\)-percentil empírico de los retornos mensuales del portafolio.

# Serie mensual de retornos del portafolio óptimo
rp_m   <- as.numeric(returns_m[, tickers] %*% w_opt)
mu_p_m <- mean(rp_m)
sd_p_m <- sd(rp_m)

# --- VaR paramétrico (normal) ---
z99 <- qnorm(0.99); z95 <- qnorm(0.95)
VaR99_param_pct <- -(mu_p_m - z99 * sd_p_m)
VaR95_param_pct <- -(mu_p_m - z95 * sd_p_m)

# --- VaR histórico ---
VaR99_hist_pct <- -as.numeric(quantile(rp_m, 0.01))
VaR95_hist_pct <- -as.numeric(quantile(rp_m, 0.05))

VaR_tab <- data.frame(
  Método        = c("Paramétrico (normal)", "Histórico"),
  `VaR 99% (%)` = scales::percent(c(VaR99_param_pct, VaR99_hist_pct), 0.01),
  `VaR 95% (%)` = scales::percent(c(VaR95_param_pct, VaR95_hist_pct), 0.01),
  `VaR 99% (USD)` = scales::dollar(c(VaR99_param_pct, VaR99_hist_pct) * capital, accuracy = 1),
  `VaR 95% (USD)` = scales::dollar(c(VaR95_param_pct, VaR95_hist_pct) * capital, accuracy = 1),
  check.names = FALSE
)
ktab(VaR_tab, "VaR mensual del portafolio óptimo")
VaR mensual del portafolio óptimo
Método VaR 99% (%) VaR 95% (%) VaR 99% (USD) VaR 95% (USD)
Paramétrico (normal) 10.69% 7.03% $2,137,659 $1,406,835
Histórico 9.56% 6.40% $1,912,610 $1,280,033

4.1 Distribución de retornos del portafolio

df_rp <- data.frame(retorno = rp_m)

p_var <- ggplot(df_rp, aes(x = retorno)) +
  geom_histogram(aes(y = after_stat(density)), bins = 30,
                 fill = "#1f77b4", color = "white", alpha = 0.8) +
  stat_function(fun = dnorm, args = list(mean = mu_p_m, sd = sd_p_m),
                color = "#ff7f0e", linewidth = 1) +
  geom_vline(xintercept = -VaR99_param_pct, color = "red",  linetype = "dashed") +
  geom_vline(xintercept = -VaR95_param_pct, color = "orange", linetype = "dashed") +
  annotate("text", x = -VaR99_param_pct, y = 0, label = "VaR 99%",
           vjust = -0.5, hjust = 1.05, color = "red", size = 3.5) +
  annotate("text", x = -VaR95_param_pct, y = 0, label = "VaR 95%",
           vjust = -0.5, hjust = 1.05, color = "orange", size = 3.5) +
  scale_x_continuous(labels = percent) +
  labs(title = "Distribución empírica de retornos mensuales del portafolio",
       subtitle = "Curva naranja: ajuste normal · Líneas: VaR paramétrico",
       x = "Retorno mensual", y = "Densidad") +
  theme_minimal(base_size = 12)

plotly::ggplotly(p_var)

Interpretación. Con 99% de confianza, las pérdidas mensuales del portafolio no superarán aproximadamente $2,137,659 (paramétrico). Esta es la magnitud que la cobertura con futuros busca atenuar; el monto cubierto con futuros sobre el índice debe ser consistente con el componente sistémico de este VaR (la fracción atribuible a β·V·σ_m).


5 CAPM y beta del portafolio

Fórmulas:

  • Beta individual (mínimos cuadrados sobre la regresión de mercado): \[ \beta_i = \dfrac{\text{Cov}(r_i, r_m)}{\text{Var}(r_m)} \]
  • Beta del portafolio: \(\beta_p = \sum_{i=1}^{3} w_i \cdot \beta_i\)
  • CAPM: \(E(r_i) = r_f + \beta_i \, (E(r_m) - r_f)\)
rm_m   <- as.numeric(returns_m[, "SPX"])
mu_m_m <- mean(rm_m)

# Beta de cada acción + R² + alpha (para análisis CAPM)
beta_stats <- lapply(tickers, function(t) {
  ri <- as.numeric(returns_m[, t])
  fit <- lm(ri ~ rm_m)
  data.frame(
    Acción = t,
    Alpha_mensual = coef(fit)[["(Intercept)"]],
    Beta = coef(fit)[["rm_m"]],
    R2 = summary(fit)$r.squared
  )
}) |> bind_rows()

# Beta ponderada del portafolio
beta_p <- sum(w_opt * beta_stats$Beta)

# Retornos esperados según CAPM (anualizados)
mu_m_a   <- mu_m_m * 12
beta_stats <- beta_stats |>
  mutate(
    `E[r] CAPM anual` = rf_a + Beta * (mu_m_a - rf_a),
    `Retorno hist. anual` = mu_a[Acción]
  )

# Tabla
beta_tab <- data.frame(
  Acción = c(tickers, "Portafolio"),
  Peso   = c(scales::percent(w_opt, 0.01), "100%"),
  Beta   = round(c(beta_stats$Beta, beta_p), 4),
  ``   = c(round(beta_stats$R2, 3), NA),
  `E[r] CAPM anual` = c(scales::percent(beta_stats$`E[r] CAPM anual`, 0.01),
                        scales::percent(rf_a + beta_p * (mu_m_a - rf_a), 0.01)),
  check.names = FALSE
)
ktab(beta_tab, "Betas, R² y retornos esperados según CAPM")
Betas, R² y retornos esperados según CAPM
Acción Peso Beta E[r] CAPM anual
CMI CMI 32.50% 1.1310 0.461 13.38%
HSY HSY 16.16% 0.2301 0.028 6.31%
CDNS CDNS 51.34% 1.0290 0.338 12.58%
Portafolio 100% 0.9331 NA 11.82%

5.1 Diagrama de regresión: retornos vs mercado

df_scatter <- data.frame(
  rm = rm_m,
  CMI  = as.numeric(returns_m[, "CMI"]),
  HSY  = as.numeric(returns_m[, "HSY"]),
  CDNS = as.numeric(returns_m[, "CDNS"])
) |>
  pivot_longer(-rm, names_to = "Acción", values_to = "ri")

p_beta <- ggplot(df_scatter, aes(x = rm, y = ri, color = Acción)) +
  geom_point(alpha = 0.5, size = 1.5) +
  geom_smooth(method = "lm", se = FALSE, linewidth = 0.8) +
  scale_x_continuous(labels = percent) +
  scale_y_continuous(labels = percent) +
  facet_wrap(~Acción, ncol = 3) +
  labs(title = "Regresión CAPM: retorno acción vs retorno S&P 500",
       x = "Retorno mensual S&P 500", y = "Retorno mensual acción") +
  theme_minimal(base_size = 11) +
  theme(legend.position = "none")

plotly::ggplotly(p_beta)

Interpretación. β del portafolio = 0.9331. Esto significa que ante un movimiento del 1% en el S&P 500, se espera que el portafolio se mueva aproximadamente 93.31% en la misma dirección. Esta β es el insumo principal para calcular el número de contratos de cobertura.


6 Número óptimo de contratos de futuros

6.1 Especificaciones del contrato — E-mini S&P 500 (CME, ticker ES)

Especificaciones E-mini S&P 500 (CME Group / TradeStation, abril 2026)
Item Valor
Activo subyacente Índice S&P 500
Bolsa CME Group (Globex)
Multiplicador USD 50 por punto
Tamaño del contrato (a F₀≈5.000) ≈ USD 250.000 (USD 50 × 5.000)
Tick mínimo 0.25 puntos
Valor del tick USD 12.50
Margen inicial overnight USD 26.500
Margen de mantenimiento USD 24.000
Liquidación Efectivo (cash-settled)
Mark-to-market Diario (académicamente: mensual, según enunciado)
Vencimientos disponibles Trimestral: H (Mar), M (Jun), U (Sep), Z (Dic)
Día de vencimiento 3er viernes del mes de vencimiento

Fuente de márgenes. Valores recientes consultados en CME Group — E-mini S&P 500 Margins, corroborados con TradeStation Futures Margin Requirements (abril 2026). Los márgenes pueden variar diariamente según volatilidad del mercado; se utilizan los valores vigentes a 30-abr-2026 para el ejercicio.

6.2 Precio del futuro a 30-abr-2026

# Descargar precio del E-mini S&P 500 continuo (ES=F en Yahoo Finance)
# Para el contrato específico ESM26 (jun-2026), usar quandl/CME directamente.
fut <- tryCatch(
  quantmod::getSymbols("ES=F", from = end_date - 30, to = end_date + 1,
                       auto.assign = FALSE, src = "yahoo"),
  error = function(e) NULL
)

if (!is.null(fut) && nrow(fut) > 0) {
  F0 <- as.numeric(last(Cl(fut)))
  fecha_F0 <- as.Date(last(index(fut)))
  fuente_F0 <- "ES=F (continuous E-mini S&P 500, Yahoo Finance)"
} else {
  # Fallback: usar el cierre del S&P 500 spot como aproximación (cost-of-carry ≈ 0)
  F0 <- as.numeric(last(prices_monthly[, "SPX"]))
  fecha_F0 <- as.Date(last(index(prices_monthly)))
  fuente_F0 <- "S&P 500 spot (^GSPC) — proxy ante fallo en ES=F"
}

cat(sprintf("Precio del futuro F₀ = %.2f puntos\nFecha: %s\nFuente: %s\n",
            F0, format(fecha_F0), fuente_F0))
## Precio del futuro F₀ = 7243.75 puntos
## Fecha: 2026-04-30
## Fuente: ES=F (continuous E-mini S&P 500, Yahoo Finance)

6.3 Cálculo del número óptimo de contratos

Fórmula (cobertura por beta): \[ N^* = \beta_p \cdot \dfrac{V_p}{F_0 \cdot m} \]

donde \(V_p = 20.000.000\) USD, \(F_0\) = precio del futuro y \(m = 50\) (multiplicador del E-mini S&P 500).

mult <- 50
N_star_exact <- beta_p * capital / (F0 * mult)
N_int_floor  <- floor(N_star_exact)
N_int_ceil   <- ceiling(N_star_exact)
N_int_round  <- round(N_star_exact)

# Cobertura efectiva con cada redondeo
beta_cubierta_floor <- N_int_floor * F0 * mult / capital
beta_cubierta_ceil  <- N_int_ceil  * F0 * mult / capital
beta_cubierta_round <- N_int_round * F0 * mult / capital

n_tab <- data.frame(
  Métrica = c("β del portafolio",
              "Valor del contrato (F₀ × m)",
              "N* exacto",
              "N* redondeado al entero más cercano",
              "N* redondeo hacia arriba (sobre-cobertura)",
              "N* redondeo hacia abajo (sub-cobertura)",
              "β efectiva cubierta (round)",
              "β efectiva cubierta (ceil)",
              "β efectiva cubierta (floor)",
              "β residual (round)"),
  Valor = c(round(beta_p, 4),
            scales::dollar(F0 * mult, accuracy = 1),
            round(N_star_exact, 4),
            N_int_round,
            N_int_ceil,
            N_int_floor,
            round(beta_cubierta_round, 4),
            round(beta_cubierta_ceil,  4),
            round(beta_cubierta_floor, 4),
            round(beta_p - beta_cubierta_round, 4))
)
ktab(n_tab, "Número óptimo de contratos de futuros E-mini S&P 500")
Número óptimo de contratos de futuros E-mini S&P 500
Métrica Valor
β del portafolio 0.9331
Valor del contrato (F₀ × m) $362,188
N* exacto 51.5246
N* redondeado al entero más cercano 52
N* redondeo hacia arriba (sobre-cobertura) 52
N* redondeo hacia abajo (sub-cobertura) 51
β efectiva cubierta (round) 0.9417
β efectiva cubierta (ceil) 0.9417
β efectiva cubierta (floor) 0.9236
β residual (round) -0.0086
N_contratos <- N_int_round  # decisión: redondeo al entero más cercano

Justificación del redondeo. Como los contratos son discretos, hay que elegir entre tres alternativas:

Criterio Resultado Implicación
Hacia arriba (ceil) 52 Sobre-cobertura: β cubierta > β del portafolio. Mayor protección si el mercado cae, pero introduce una exposición negativa neta al mercado.
Hacia abajo (floor) 51 Sub-cobertura: queda β residual positiva = 0.0095. El portafolio sigue parcialmente expuesto a movimientos del índice.
Más cercano (round) 52 Decisión adoptada: minimiza la diferencia absoluta entre β cubierta y β del portafolio (β residual = -0.0086).

Para un portafolio de cobertura “neutral” lo apropiado es redondear al entero más cercano, ya que ni la sobre-cobertura ni la sub-cobertura son objetivos del ejercicio. Si el gerente tuviera una visión bajista marcada, justificaría sobre-cobertura; si tuviera visión alcista pero requiere protección mínima, justificaría sub-cobertura.


7 Posición a tomar en futuros

TODO. Discutir posición corta vs larga:

  • Inversionista que ya posee acciones (este caso): posición corta en futuros sobre el índice.
  • Riesgo cubierto: caída sistémica del mercado (riesgo no diversificable, beta).
  • Si el mercado sube: la cartera gana, pero la posición corta en futuros pierde → ganancia neta atenuada.
  • Si el mercado baja: la cartera pierde, pero la posición corta gana → pérdida neta atenuada.
  • Efecto neto: se elimina (en gran parte) la exposición sistémica; permanece el riesgo idiosincrático de las 3 acciones.
  • Cuándo se tomaría posición larga: si se quisiera anticipar una compra futura con el dinero aún no disponible (cobertura “anticipatoria”).

8 Flujos mensuales y llamados al margen

Mecánica del margen (CME):

  1. Saldo inicial = N × margen inicial.
  2. Cada mes: P&L = ±N × m × ΔF (signo según posición: + larga, − corta).
  3. Saldo nuevo = saldo previo + P&L.
  4. Si saldo < N × margen mantenimiento → margin call: depositar la diferencia hasta restaurar el margen inicial.
  5. Como el enunciado indica liquidación mensual con fines académicos, ignoramos el ajuste diario.

Escenario de evaluación. Como la inversión inicia el 30-abr-2026 y el horizonte es de 4 años (abril 2030), no se dispone de precios futuros reales. Para fines didácticos se utiliza la trayectoria histórica de los últimos 48 meses del E-mini S&P 500 como escenario representativo (2022-04 a 2026-04), lo que cubre el bear market de 2022 y la recuperación 2023–2025.

# Trayectoria histórica de 48 meses de ES=F (proxy del horizonte 4 años)
fut_hist <- quantmod::getSymbols(
  "ES=F",
  from = end_date - 365 * 5,  # margen extra para asegurar 48 cierres mensuales
  to   = end_date + 1,
  auto.assign = FALSE,
  src = "yahoo"
)
fut_monthly_hist <- apply.monthly(Cl(fut_hist), last) |> na.omit()
fut_path <- tail(fut_monthly_hist, 49)  # 49 puntos -> 48 ΔF
# Parámetros del contrato (CME Group, abril 2026)
margen_inicial <- 26500    # USD por contrato — CME exchange minimum overnight
margen_mant    <- 24000    # USD por contrato
N              <- N_contratos
posicion       <- "Corta"  # cobertura típica para inversionista que ya posee acciones

precios <- as.numeric(fut_path)
fechas  <- as.Date(index(fut_path))

# Inicialización
saldo            <- N * margen_inicial
flujo            <- vector("list", length(precios) - 1)
saldo_path       <- numeric(length(precios) - 1)
reposicion_total <- 0
n_calls          <- 0

for (i in 2:length(precios)) {
  F_ini   <- precios[i-1]
  F_fin   <- precios[i]
  delta   <- F_fin - F_ini

  # P&L según posición
  PL_corta <- -N * mult * delta
  PL_larga <-  N * mult * delta
  PL <- if (posicion == "Corta") PL_corta else PL_larga

  saldo <- saldo + PL

  # Margin call si saldo < margen de mantenimiento
  margin_call <- saldo < N * margen_mant
  reposicion  <- if (margin_call) (N * margen_inicial - saldo) else 0
  if (margin_call) {
    saldo            <- saldo + reposicion
    reposicion_total <- reposicion_total + reposicion
    n_calls          <- n_calls + 1
  }

  saldo_path[i-1] <- saldo
  flujo[[i-1]] <- data.frame(
    Fecha       = fechas[i],
    F_inicial   = round(F_ini, 2),
    F_final     = round(F_fin, 2),
    `Delta F`   = round(delta, 2),
    `P&L corta` = round(PL_corta, 0),
    `P&L larga` = round(PL_larga, 0),
    Saldo       = round(saldo, 0),
    `Margin call?` = if (margin_call) "Sí" else "No",
    Reposición  = round(reposicion, 0),
    check.names = FALSE
  )
}
flujo_df <- bind_rows(flujo)

# Métricas resumen
PL_acum <- sum(flujo_df$`P&L corta`)
saldo_final <- last(saldo_path)

resumen_marg <- data.frame(
  Métrica = c("Posición", "N° de contratos", "Margen inicial total",
              "Margen de mantenimiento total", "Meses con margin call",
              "Reposición total acumulada", "P&L acumulado (posición tomada)",
              "Saldo final del margen"),
  Valor = c(posicion,
            N,
            scales::dollar(N * margen_inicial, accuracy = 1),
            scales::dollar(N * margen_mant,    accuracy = 1),
            n_calls,
            scales::dollar(reposicion_total, accuracy = 1),
            scales::dollar(PL_acum, accuracy = 1),
            scales::dollar(saldo_final, accuracy = 1))
)
ktab(resumen_marg, "Resumen de la cuenta de margen (48 meses)")
Resumen de la cuenta de margen (48 meses)
Métrica Valor
Posición Corta
N° de contratos 52
Margen inicial total $1,378,000
Margen de mantenimiento total $1,248,000
Meses con margin call 18
Reposición total acumulada $8,102,250
P&L acumulado (posición tomada) -$8,102,250
Saldo final del margen $1,378,000

8.1 Tabla mes a mes

8.2 Evolución del saldo del margen

df_saldo <- data.frame(
  Fecha = flujo_df$Fecha,
  Saldo = flujo_df$Saldo,
  `Margen mant.` = N * margen_mant,
  `Margen inicial` = N * margen_inicial,
  check.names = FALSE
) |>
  pivot_longer(-Fecha, names_to = "Línea", values_to = "USD")

p_saldo <- ggplot(df_saldo, aes(x = Fecha, y = USD, color = Línea)) +
  geom_line(linewidth = 0.8) +
  scale_y_continuous(labels = dollar) +
  scale_color_manual(values = c("Saldo" = "#1f77b4",
                                "Margen mant." = "red",
                                "Margen inicial" = "darkgreen")) +
  labs(title = "Cuenta de margen mensual — posición corta en E-mini S&P 500",
       x = NULL, y = "USD", color = NULL) +
  theme_minimal(base_size = 12)

plotly::ggplotly(p_saldo)

9 Estrategia de roll-over trimestral con contratos específicos

Mecánica. Como los contratos E-mini S&P 500 vencen trimestralmente (H/M/U/Z, tercer viernes), una cobertura de 4 años requiere 16 contratos consecutivos. En cada vencimiento:

  1. Cierre de la posición corta en el contrato vigente al precio de liquidación final (cash settlement, igual al spot del S&P 500 al vencimiento).
  2. Apertura simultánea de nueva posición corta en el siguiente contrato disponible (ej: al vencer ESM26 se abre ESU26).
  3. Realización del P&L acumulado del contrato cerrado.
  4. Riesgo de base: diferencia entre el precio de cierre del contrato saliente y el de apertura del entrante. Surge del cost-of-carry entre vencimientos: \(F(t,T) = S(t) \cdot e^{(r_f - q)(T-t)}\).

9.1 Modelo de cost-of-carry

# Parámetros de cost-of-carry
r_carry <- rf_a            # tasa libre de riesgo (de sección 10)
q_div   <- 0.014           # dividend yield del S&P 500 (~1.4% anual histórico)
carry_q <- exp((r_carry - q_div) * 0.25) - 1   # carry por trimestre

cat(sprintf("Tasa libre de riesgo (r):  %s\nDividend yield S&P (q):    %s\nCarry trimestral (r-q):    %s\n",
            scales::percent(r_carry, 0.01),
            scales::percent(q_div, 0.01),
            scales::percent(carry_q, 0.001)))
## Tasa libre de riesgo (r):  4.50%
## Dividend yield S&P (q):    1.40%
## Carry trimestral (r-q):    0.778%

9.2 Tabla de los 16 contratos

# Nombres de los 16 contratos del programa de cobertura 2026-2030
# Secuencia: M26, U26, Z26, H27, M27, U27, Z27, H28, M28, U28, Z28, H29, M29, U29, Z29, H30
codigos_full <- c("M","U","Z","H")    # Jun, Sep, Dic, Mar
contrato_nombres <- vapply(seq_len(16), function(k) {
  cc <- codigos_full[((k - 1) %% 4) + 1]
  yy <- if (k <= 3)        26
        else if (k <= 7)   27
        else if (k <= 11)  28
        else if (k <= 15)  29
        else               30
  sprintf("ES%s%02d", cc, yy)
}, character(1))

# Spot path: utilizamos el proxy histórico de 49 puntos mensuales (48 ΔF)
# Cada contrato cubre 3 meses: contratos 1-16, 3 meses cada uno = 48 meses
spot_path <- as.numeric(fut_path)
fechas_path <- as.Date(index(fut_path))

# Para cada contrato k (3 meses): F_open = spot * (1+carry); F_close = spot final (convergencia)
contratos_df <- map_dfr(seq_len(16), function(k) {
  start_idx <- (k - 1) * 3 + 1
  end_idx   <- k * 3 + 1
  if (end_idx > length(spot_path)) end_idx <- length(spot_path)

  spot_open  <- spot_path[start_idx]
  spot_close <- spot_path[end_idx]

  F_open  <- spot_open  * exp((r_carry - q_div) * 0.25)   # cost-of-carry sobre 3m
  F_close <- spot_close                                   # cash settlement = spot al vencimiento

  PL_corta <- -N_contratos * mult * (F_close - F_open)
  PL_larga <-  N_contratos * mult * (F_close - F_open)

  # Descomposición del P&L corta:
  # PL_corta = N*m*(F_open - F_close) = carry_pickup + componente_direccional
  carry_pickup <- N_contratos * mult * (F_open - spot_open)         # ganancia "garantizada" del corto
  direccional  <- -N_contratos * mult * (spot_close - spot_open)    # ganancia direccional (neg si mercado sube)

  data.frame(
    Contrato        = contrato_nombres[k],
    `Fecha apertura`= fechas_path[start_idx],
    `Fecha cierre`  = fechas_path[end_idx],
    `Spot apertura` = round(spot_open, 2),
    `F apertura`    = round(F_open, 2),
    `F cierre`      = round(F_close, 2),
    `Cambio F`      = round(F_close - F_open, 2),
    `Carry pickup`  = round(carry_pickup, 0),
    `Direccional`   = round(direccional, 0),
    `P&L corta`     = round(PL_corta, 0),
    check.names = FALSE
  )
})
contratos_df$`P&L acum corta` <- cumsum(contratos_df$`P&L corta`)

DT::datatable(
  contratos_df |>
    mutate(across(c(`Carry pickup`, `Direccional`, `P&L corta`, `P&L acum corta`),
                  ~ scales::dollar(., accuracy = 1))),
  options = list(pageLength = 16, scrollX = TRUE,
                 columnDefs = list(list(className = "dt-center", targets = "_all"))),
  rownames = FALSE,
  caption = "Lifecycle de cada uno de los 16 contratos del programa de roll-over"
)

9.3 Riesgo de base en cada roll

En cada vencimiento, el contrato saliente liquida al spot (\(F_{cierre} = S\)) y el entrante abre a \(F_{apertura} = S \cdot e^{(r-q) \cdot 0.25}\). El gap de roll es la base risk realizada:

gaps_df <- contratos_df |>
  mutate(
    F_apertura_siguiente = lead(`F apertura`, default = NA_real_),
    `Gap de roll (pts)`  = F_apertura_siguiente - `F cierre`,
    `Gap (%)`            = `Gap de roll (pts)` / `F cierre`
  ) |>
  filter(!is.na(`Gap de roll (pts)`)) |>
  select(Contrato_saliente = Contrato,
         `F cierre`,
         F_apertura_siguiente,
         `Gap de roll (pts)`,
         `Gap (%)`)

ktab(
  gaps_df |>
    mutate(`Gap (%)` = scales::percent(`Gap (%)`, 0.01)),
  "Gap de roll-over: diferencia entre F del saliente y F del entrante"
)
Gap de roll-over: diferencia entre F del saliente y F del entrante
Contrato_saliente F cierre F_apertura_siguiente Gap de roll (pts) Gap (%)
ESM26 4133.50 4165.66 32.16 0.78%
ESU26 3883.00 3913.21 30.21 0.78%
ESZ26 4090.00 4121.82 31.82 0.78%
ESH27 4188.50 4221.09 32.59 0.78%
ESM27 4614.50 4650.40 35.90 0.78%
ESU27 4212.25 4245.02 32.77 0.78%
ESZ27 4870.50 4908.39 37.89 0.78%
ESH28 5067.00 5106.42 39.42 0.78%
ESM28 5558.00 5601.24 43.24 0.78%
ESU28 5738.50 5783.15 44.65 0.78%
ESZ28 6067.25 6114.45 47.20 0.78%
ESH29 5587.00 5630.47 43.47 0.78%
ESM29 6374.25 6423.84 49.59 0.78%
ESU29 6874.00 6927.48 53.48 0.78%
ESZ29 6965.75 7019.94 54.19 0.78%

Lectura económica. El “carry pickup” es la ganancia que el corto obtiene independientemente de la dirección del mercado, equivalente a aprovechar la diferencia entre la tasa libre de riesgo y el dividend yield del índice. En un mercado neutral (sin movimiento del spot), cada contrato vendido en corto cierra con un P&L positivo igual a este componente.

9.4 P&L trimestral acumulado

df_pl <- contratos_df |>
  mutate(Periodo = row_number()) |>
  select(Periodo, Contrato, `Carry pickup`, `Direccional`, `P&L corta`, `P&L acum corta`) |>
  pivot_longer(c(`Carry pickup`, `Direccional`),
               names_to = "Componente", values_to = "USD")

p_roll <- ggplot(df_pl, aes(x = Periodo, y = USD, fill = Componente)) +
  geom_bar(stat = "identity", position = "stack") +
  geom_line(data = contratos_df |> mutate(Periodo = row_number()),
            aes(x = Periodo, y = `P&L acum corta`),
            inherit.aes = FALSE, color = "#d62728", linewidth = 1.2) +
  geom_point(data = contratos_df |> mutate(Periodo = row_number()),
             aes(x = Periodo, y = `P&L acum corta`),
             inherit.aes = FALSE, color = "#d62728", size = 2) +
  scale_y_continuous(labels = dollar) +
  scale_x_continuous(breaks = 1:16, labels = contrato_nombres) +
  scale_fill_manual(values = c("Carry pickup" = "#2ca02c", "Direccional" = "#1f77b4")) +
  labs(title = "Descomposición del P&L de la cobertura corta — 16 contratos",
       subtitle = "Barras: P&L por contrato (carry + direccional). Línea roja: P&L acumulado.",
       x = NULL, y = "USD", fill = NULL) +
  theme_minimal(base_size = 11) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

plotly::ggplotly(p_roll)

9.5 Riesgo de base — futuro vs spot histórico

spx_monthly <- prices_monthly[, "SPX"]
spx_path    <- as.numeric(tail(spx_monthly, length(precios)))

base_df <- data.frame(
  Fecha = fechas,
  F_futuro = precios,
  Spot_SPX = spx_path,
  Base = precios - spx_path
)

p_base <- ggplot(base_df, aes(x = Fecha)) +
  geom_line(aes(y = F_futuro, color = "Futuro ES")) +
  geom_line(aes(y = Spot_SPX, color = "Spot S&P 500")) +
  scale_color_manual(values = c("Futuro ES" = "#1f77b4", "Spot S&P 500" = "#ff7f0e")) +
  scale_y_continuous(labels = comma) +
  labs(title = "Futuro vs Spot — magnitud histórica del riesgo de base",
       x = NULL, y = "Puntos del índice", color = NULL) +
  theme_minimal(base_size = 12)

plotly::ggplotly(p_base)

9.6 Escenarios extremos

Estrategia Comportamiento ante mercado alcista Comportamiento ante mercado bajista Sentido económico
Siempre corta (cobertura permanente) Pierde en componente direccional pero gana carry pickup en cada roll Compensa pérdidas del portafolio + gana carry Tiene sentido cuando se desea aislar generación de alfa del riesgo de mercado
Siempre larga Duplica la exposición sistémica + paga carry en cada roll Duplica las pérdidas + paga carry No es cobertura — es apalancamiento sintético
Roll-over disciplinado (caso adoptado) Ganancia neta = α + dividendos + carry − componente direccional adverso Pérdidas atenuadas en β·V + carry pickup Estándar de mercado para coberturas plurianuales

10 Valor esperado bajo cobertura — tasa libre de riesgo

Fundamento. En una cobertura perfecta de β, el portafolio cubierto debería rendir aproximadamente la tasa libre de riesgo, ya que el componente sistémico (β·μ_m) queda neutralizado y solo permanece la rentabilidad del activo libre de riesgo más el alfa del portafolio.

\[ E[r_{cubierto}] \approx r_f + \alpha_p \qquad E[V_{cubierto}^{trimestre}] = V_p \cdot (1 + r_f \cdot 0.25) \]

# ^FVX: CBOE 5Y Treasury Yield (rango 3-5y, según indicación del enunciado)
fvx <- tryCatch(
  quantmod::getSymbols("^FVX", from = end_date - 30, to = end_date + 1,
                       auto.assign = FALSE, src = "yahoo"),
  error = function(e) NULL
)

if (!is.null(fvx) && nrow(fvx) > 0) {
  rf_a_obs <- as.numeric(last(Cl(fvx))) / 100
  fecha_rf <- as.Date(last(index(fvx)))
  fuente_rf <- "^FVX (CBOE 5Y Treasury Yield, Yahoo Finance)"
} else {
  rf_a_obs <- 0.045
  fecha_rf <- end_date
  fuente_rf <- "Placeholder 4.5% (fallback)"
}

# Actualizar la tasa libre de riesgo con el valor observado
rf_a <- rf_a_obs
rf_q <- rf_a / 4
rf_m <- rf_a / 12

cat(sprintf("Tasa libre de riesgo anualizada (rf): %s\nFecha: %s\nFuente: %s\n",
            scales::percent(rf_a, 0.01), format(fecha_rf), fuente_rf))
## Tasa libre de riesgo anualizada (rf): 4.02%
## Fecha: 2026-04-30
## Fuente: ^FVX (CBOE 5Y Treasury Yield, Yahoo Finance)
# Valor esperado del portafolio bajo distintos enfoques (un trimestre)
horizonte_q <- 0.25  # un trimestre

# 1. Sin cobertura: capitaliza al retorno esperado del portafolio
ret_q_sin_cob <- (1 + ret_p)^horizonte_q - 1
V_sin_cob <- capital * (1 + ret_q_sin_cob)

# 2. Cubierto: rinde ≈ rf
V_cubierto <- capital * (1 + rf_q)

# 3. Reducción de riesgo
sd_q_sin_cob <- sd_p * sqrt(horizonte_q)
sd_q_cubierto <- sd_q_sin_cob * sqrt(1 - 0.95)  # ~95% de la varianza sistémica eliminada (proxy)

ve_tab <- data.frame(
  Escenario = c("Sin cobertura", "Cubierto con futuros", "Diferencia"),
  `Retorno trim.` = c(scales::percent(ret_q_sin_cob, 0.01),
                       scales::percent(rf_q, 0.01),
                       scales::percent(rf_q - ret_q_sin_cob, 0.01)),
  `Valor esperado` = c(scales::dollar(V_sin_cob, 1),
                        scales::dollar(V_cubierto, 1),
                        scales::dollar(V_cubierto - V_sin_cob, 1)),
  `Volatilidad trim.` = c(scales::percent(sd_q_sin_cob, 0.01),
                           scales::percent(sd_q_cubierto, 0.01),
                           "≈ −95%"),
  check.names = FALSE
)
ktab(ve_tab, "Valor esperado de la cobertura trimestral")
Valor esperado de la cobertura trimestral
Escenario Retorno trim. Valor esperado Volatilidad trim.
Sin cobertura 4.97% $20,994,561 9.29%
Cubierto con futuros 1.01% $20,201,150 2.08%
Diferencia -3.97% -$793,411 ≈ −95%

Interpretación. La cobertura reduce sustancialmente la volatilidad del portafolio (al eliminar la varianza sistémica) pero sacrifica retorno esperado porque el inversionista renuncia a la prima de riesgo del mercado (μ_m − r_f) sobre la fracción cubierta. Si el portafolio tiene α > 0, el resultado neto es α + r_f, lo que justifica la cobertura cuando el manager confía en su capacidad de generar alfa sin querer estar expuesto al mercado.


11 Comparación: sin cobertura, cubierto, ajustado por VaR

# Horizontes: 1 mes y 48 meses (4 años) capitalizando mensualmente
n_meses <- 48

# --- 1. Sin cobertura ---
mu_p_m_calc <- ret_p / 12
sd_p_m_calc <- sd_p / sqrt(12)
E_sin_cob_1m  <- capital * (1 + mu_p_m_calc)
E_sin_cob_4y  <- capital * (1 + mu_p_m_calc)^n_meses
sd_sin_cob_4y <- capital * sd_p_m_calc * sqrt(n_meses)

# --- 2. Cubierto con futuros (rinde ~ rf) ---
E_cub_1m  <- capital * (1 + rf_m)
E_cub_4y  <- capital * (1 + rf_m)^n_meses
sd_cub_4y <- E_cub_4y * 0.05  # asumiendo varianza residual ~5% (idiosincrática)

# --- 3. Ajustado por VaR (escenario adverso al 99%) ---
# Caso conservador: el portafolio realiza el VaR mensual y luego sigue trayectoria normal
E_var_1m <- capital * (1 - VaR99_param_pct)
# A 4 años: mensual normal pero con drift recortado en VaR/12
E_var_4y <- capital * (1 + (mu_p_m_calc - VaR99_param_pct/12))^n_meses

comparacion <- data.frame(
  Escenario = c("Sin cobertura", "Cubierto con futuros", "Ajustado por VaR 99%"),
  `Valor esperado 1 mes` = c(scales::dollar(E_sin_cob_1m, 1),
                              scales::dollar(E_cub_1m,    1),
                              scales::dollar(E_var_1m,    1)),
  `Retorno mensual` = c(scales::percent(mu_p_m_calc, 0.01),
                         scales::percent(rf_m, 0.01),
                         scales::percent(-VaR99_param_pct, 0.01)),
  `Valor esperado 4 años` = c(scales::dollar(E_sin_cob_4y, 1),
                               scales::dollar(E_cub_4y,    1),
                               scales::dollar(E_var_4y,    1)),
  `Retorno acumulado 4y` = c(scales::percent(E_sin_cob_4y/capital - 1, 0.01),
                              scales::percent(E_cub_4y/capital - 1, 0.01),
                              scales::percent(E_var_4y/capital - 1, 0.01)),
  check.names = FALSE
)
ktab(comparacion, "Comparación de escenarios — 1 mes y horizonte de 4 años")
Comparación de escenarios — 1 mes y horizonte de 4 años
Escenario Valor esperado 1 mes Retorno mensual Valor esperado 4 años Retorno acumulado 4y
Sin cobertura $20,357,079 1.79% $46,766,253 133.83%
Cubierto con futuros $20,067,050 0.34% $23,485,498 17.43%
Ajustado por VaR 99% $17,862,341 -10.69% $30,669,872 53.35%
# Trayectorias proyectadas (path determinístico al retorno esperado)
meses <- 0:n_meses
df_path <- tibble(
  Mes = meses,
  `Sin cobertura` = capital * (1 + mu_p_m_calc)^meses,
  `Cubierto`      = capital * (1 + rf_m)^meses,
  `Ajustado VaR`  = capital * (1 + (mu_p_m_calc - VaR99_param_pct/12))^meses
) |>
  pivot_longer(-Mes, names_to = "Escenario", values_to = "Valor")

p_comp <- ggplot(df_path, aes(x = Mes, y = Valor, color = Escenario)) +
  geom_line(linewidth = 1) +
  scale_y_continuous(labels = dollar) +
  scale_color_manual(values = c("Sin cobertura" = "#1f77b4",
                                "Cubierto"      = "#2ca02c",
                                "Ajustado VaR"  = "#d62728")) +
  labs(title = "Trayectoria esperada del valor del portafolio (4 años)",
       x = "Mes desde inicio", y = "Valor esperado (USD)", color = NULL) +
  theme_minimal(base_size = 12)

plotly::ggplotly(p_comp)

11.1 Tratamiento de dividendos

Acción Política de dividendos Yield aprox. Impacto en cálculos
CMI Trimestral, creciente ~2.5% Incorporado vía precios ajustados
HSY Trimestral, >50 años de pago ~2.7% Incorporado vía precios ajustados
CDNS No paga dividendos (reinvierte en R&D) 0% N/A

Importante. Los retornos utilizados en este informe se calculan sobre precios ajustados (Adjusted Close de Yahoo Finance), que ya incorporan los dividendos pagados como retorno total reinvertido. Por lo tanto, no se requiere ajuste adicional. Para el portafolio óptimo, el ingreso por dividendos ponderado se aproxima:

\[ d_p \approx w_{CMI} \cdot 2.5\% + w_{HSY} \cdot 2.7\% + w_{CDNS} \cdot 0\% \]

Esta porción del retorno es insensible a la cobertura con futuros sobre el índice, ya que los dividendos se cobran independientemente del precio del subyacente.


12 Sensibilidad por beta (β = 0.5 y β = 2)

Fórmula: \(N^* = \beta \cdot V_p / (F_0 \cdot m)\). Manteniendo \(V_p\), \(F_0\) y \(m\) constantes, \(N^*\) es lineal en β.

betas_hip <- c(0.5, beta_p, 2.0)
etiquetas <- c("Hipotético β = 0.5",
               sprintf("Real β = %.3f (calculado)", beta_p),
               "Hipotético β = 2.0")

sens <- tibble(
  Escenario = etiquetas,
  Beta = betas_hip,
  `N* exacto`   = round(betas_hip * capital / (F0 * mult), 4),
  `N* entero`   = round(betas_hip * capital / (F0 * mult)),
  `Exposición sistémica (USD)` = betas_hip * capital,
  `Sensibilidad por +1% S&P` = betas_hip * 0.01 * capital,
  `Margen inicial requerido` = round(betas_hip * capital / (F0 * mult)) * margen_inicial,
  `Margen mant. requerido`   = round(betas_hip * capital / (F0 * mult)) * margen_mant
) |>
  mutate(
    `Exposición sistémica (USD)` = scales::dollar(`Exposición sistémica (USD)`, 1),
    `Sensibilidad por +1% S&P`   = scales::dollar(`Sensibilidad por +1% S&P`, 1),
    `Margen inicial requerido`   = scales::dollar(`Margen inicial requerido`, 1),
    `Margen mant. requerido`     = scales::dollar(`Margen mant. requerido`, 1)
  )

ktab(sens, "Sensibilidad de la cobertura ante distintos β del portafolio")
Sensibilidad de la cobertura ante distintos β del portafolio
Escenario Beta N* exacto N* entero Exposición sistémica (USD) Sensibilidad por +1% S&P Margen inicial requerido Margen mant. requerido
Hipotético β = 0.5 0.5000000 27.6100 28 $10,000,000 $100,000 $742,000 $672,000
Real β = 0.933 (calculado) 0.9330791 51.5246 52 $18,661,582 $186,616 $1,378,000 $1,248,000
Hipotético β = 2.0 2.0000000 110.4400 110 $40,000,000 $400,000 $2,915,000 $2,640,000
betas_grid <- seq(0.1, 2.5, by = 0.05)
df_sens <- tibble(
  beta = betas_grid,
  N_exacto = beta * capital / (F0 * mult),
  Margen_total = round(N_exacto) * margen_inicial
)

p_sens <- ggplot(df_sens, aes(x = beta)) +
  geom_line(aes(y = N_exacto), color = "#1f77b4", linewidth = 1) +
  geom_vline(xintercept = c(0.5, beta_p, 2.0), linetype = "dashed",
             color = c("gray50", "red", "gray50")) +
  annotate("text", x = beta_p, y = max(df_sens$N_exacto) * 0.95,
           label = sprintf("β real = %.3f", beta_p), color = "red", hjust = -0.1) +
  scale_x_continuous(breaks = seq(0, 2.5, 0.25)) +
  labs(title = "N° óptimo de contratos en función de β del portafolio",
       x = "β del portafolio", y = "N* (exacto)") +
  theme_minimal(base_size = 12)

plotly::ggplotly(p_sens)

Interpretación financiera.

  • β = 0.5 (defensivo): el portafolio es la mitad de sensible que el índice → requiere 28 contratos, la mitad del caso real. Menor costo de margen, menor protección absoluta, pero también menor “ruido” introducido por la cobertura.
  • β = 2.0 (agresivo): el portafolio amplifica los movimientos del índice → requiere 110 contratos, el cuádruple del caso β=0.5. Mayor margen requerido, pero también mayor protección ante caídas. Un portafolio con β = 2 perdería ~40% si el mercado cae 20%, frente a la pérdida sin cobertura.
  • Linealidad: N* escala proporcionalmente con β, pero el margen total escala con N* redondeado (función escalón), por lo que en β bajos el redondeo introduce mayor error relativo de cobertura.
  • Conclusión: un mayor β justifica una cobertura más intensa, pero también señala un portafolio más volátil → el manager debe evaluar si la concentración de riesgo sistémico es deseable o si conviene primero rebalancear hacia activos con menor β antes de cubrir.

13 Conclusiones

Construcción del portafolio. A partir del universo del S&P 500 se seleccionaron tres acciones con perfiles complementarios y baja correlación esperada: Cummins (CMI) como exposición cíclica industrial con opcionalidad en transición energética, Hershey (HSY) como activo defensivo con dividendo creciente, y Cadence (CDNS) como crecimiento secular en automatización de diseño de semiconductores. La optimización media-varianza generó un portafolio de tangencia cuyo Sharpe Ratio es superior al de cualquier acción individual, validando empíricamente el beneficio de la diversificación.

Riesgo del portafolio sin cobertura. El VaR mensual al 99% (paramétrico) representa una pérdida potencial significativa frente al capital de USD 20 M. La β del portafolio (combinación ponderada de las β individuales) constituye el insumo central para dimensionar la cobertura: cuanta más β, más sensible al mercado, más contratos requiere la cobertura.

Diseño de la cobertura. El número óptimo de contratos E-mini S&P 500 surge de \(N^* = \beta_p \cdot V_p / (F_0 \cdot m)\) y se redondea al entero más cercano para minimizar la β residual del portafolio cubierto. La posición es corta en futuros, consistente con un inversionista que ya posee acciones y desea protegerse de una caída del mercado.

Mecánica operativa (margen y roll-over). El análisis mes a mes sobre 48 períodos históricos muestra que la cuenta de margen experimenta llamados al margen en períodos de fuerte alza del mercado (donde la posición corta pierde), exigiendo reposiciones que comprometen liquidez. La estrategia de roll-over trimestral implica 16 cierres y aperturas durante el horizonte de 4 años, generando costos transaccionales y exposición a riesgo de base que, para futuros cash-settled como el E-mini, es bajo en magnitud.

Trade-off de la cobertura. La comparación entre los escenarios sin cobertura, cubierto y ajustado por VaR evidencia el principio fundamental: la cobertura sustituye la prima de riesgo del mercado por la tasa libre de riesgo. Esto reduce sustancialmente la volatilidad y el VaR, pero también reduce el retorno esperado. La cobertura tiene sentido cuando (a) el manager confía en su capacidad de generar alfa y quiere aislarla del mercado, (b) el horizonte coincide con un período de incertidumbre macroeconómica elevada, (c) el costo de oportunidad de no cubrir (pérdida potencial extrema) supera la prima de riesgo cedida.

Recomendación al gerente. Para el portafolio diseñado y bajo las condiciones de mercado actuales:

  1. Implementar cobertura parcial, no del 100% del portafolio: cubrir aproximadamente el componente sistémico permite preservar el alfa esperado de la selección de las tres acciones.
  2. Disciplina en roll-over trimestral: ejecutar el roll en los 5 días previos al vencimiento para minimizar liquidez baja y spreads amplios.
  3. Monitoreo continuo de β: la β del portafolio puede cambiar con el tiempo (cambios en correlaciones, fundamentales) — recalcular trimestralmente y ajustar N° de contratos en cada roll-over.
  4. Reservar liquidez para llamados al margen: con el N° de contratos calculado, la reposición acumulada puede ser material en un mercado fuertemente alcista. Mantener al menos 2–3× el margen inicial en efectivo o T-Bills.
  5. Revisar la cobertura ante eventos: en presencia de shocks (subida de tasas, recesión, crisis geopolítica), evaluar si conviene incrementar (β > β actual) o reducir (β < β actual) la cobertura.

14 Referencias (APA 7)

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

Cadence Design Systems. (2025). Annual Report 2024 (Form 10-K). https://investor.cadence.com/financial-information/annual-reports

CME Group. (2026). E-mini S&P 500 futures contract specifications. https://www.cmegroup.com/markets/equities/sp/e-mini-sandp500.contractSpecs.html

CME Group. (2026). E-mini S&P 500 margins. https://www.cmegroup.com/markets/equities/sp/e-mini-sandp500.margins.html

Cummins Inc. (2025). Annual Report 2024 (Form 10-K). https://investor.cummins.com/financial-information/annual-reports

Fabozzi, F. J., Markowitz, H. M., & Gupta, F. (2008). Portfolio selection. In F. J. Fabozzi (Ed.), Handbook of finance (Vol. 2, pp. 3–13). John Wiley & Sons.

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

Jorion, P. (2007). Value at risk: The new benchmark for managing financial risk (3rd ed.). McGraw-Hill.

Markowitz, H. (1952). Portfolio selection. The Journal of Finance, 7(1), 77–91. https://doi.org/10.1111/j.1540-6261.1952.tb01525.x

Ryan, J. A., & Ulrich, J. M. (2024). quantmod: Quantitative financial modelling framework (R package version 0.4.26) [Software]. https://cran.r-project.org/package=quantmod

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.1111/j.1540-6261.1964.tb02865.x

SlickCharts. (2026). S&P 500 components by weight. https://www.slickcharts.com/sp500

The Hershey Company. (2025). Annual Report 2024 (Form 10-K). https://www.thehersheycompany.com/en_us/investors/financial-reports.html

U.S. Securities and Exchange Commission. (2026). EDGAR — Cummins, Hershey, Cadence filings. https://www.sec.gov/edgar/searchedgar/companysearch

Yahoo Finance. (2026). Historical market data — CMI, HSY, CDNS, ^GSPC, ^FVX, ES=F. https://finance.yahoo.com/