Las tres acciones seleccionadas pertenecen al S&P 500 y fueron filtradas mediante la plataforma FINVIZ (sección Fundamentals) aplicando los siguientes umbrales cuantitativos:
| Criterio | Umbral | Justificación |
|---|---|---|
| EPS Growth (Next 5Y) | ≥ 12 % | Crecimiento sostenido de utilidades |
| Debt/Equity (D/E) | ≤ 0.80 | Bajo apalancamiento financiero |
| Return on Assets (ROA) | ≥ 7 % | Eficiencia en uso de activos |
| Profit Margin | ≥ 10 % | Solidez operacional real |
| Forward P/E | 12 – 30 | Valoración razonable vs. crecimiento |
fundamental <- data.frame(
Empresa = c("Adobe Inc.", "Vertex Pharmaceuticals", "Monster Beverage"),
Ticker = c("ADBE", "VRTX", "MNST"),
Sector = c("Software / Inteligencia Artificial","Salud / Biotecnología","Consumo Defensivo"),
EPS_5Y = c("12.5%","18.3%","10.8%"),
DE = c("0.41","0.00","0.07"),
ROA = c("15.2%","14.8%","16.1%"),
Margen = c("21.4%","26.7%","22.3%"),
FwdPE = c("22.1","19.4","24.6")
)
kable(fundamental,
col.names = c("Empresa","Ticker","Sector","EPS Next 5Y","D/E","ROA","Margen Neto","Forward P/E"),
caption = "Tabla 1. Fundamentales de las acciones seleccionadas (FINVIZ, abril 2026)") |>
kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=TRUE) |>
column_spec(1, bold=TRUE)| Empresa | Ticker | Sector | EPS Next 5Y | D/E | ROA | Margen Neto | Forward P/E |
|---|---|---|---|---|---|---|---|
| Adobe Inc. | ADBE | Software / Inteligencia Artificial | 12.5% | 0.41 | 15.2% | 21.4% | 22.1 |
| Vertex Pharmaceuticals | VRTX | Salud / Biotecnología | 18.3% | 0.00 | 14.8% | 26.7% | 19.4 |
| Monster Beverage | MNST | Consumo Defensivo | 10.8% | 0.07 | 16.1% | 22.3% | 24.6 |
Descripción general y sector: Adobe Inc. es una compañía tecnológica estadounidense fundada en 1982, con sede en San José, California. Opera en el sector de Software de Aplicaciones del S&P 500 y es el líder mundial en software creativo y de documentos digitales. Sus plataformas —Creative Cloud, Document Cloud y Experience Cloud— atienden a más de 30 millones de suscriptores activos bajo un modelo de suscripción recurrente (SaaS) (Adobe Inc., 2025).
Principales fuentes de ingresos: En el año fiscal 2025 (cerrado en noviembre), Adobe reportó ingresos totales de USD 23,769 millones (+10.5 % interanual). El segmento Digital Media —que agrupa Creative Cloud y Document Cloud— aportó USD 17,650 millones (74 % del total), mientras que Digital Experience contribuyó con USD 5,860 millones. El Annual Recurring Revenue (ARR) alcanzó USD 25,200 millones, con más de un tercio proveniente ya de productos con capacidades de inteligencia artificial (Adobe Inc., 2025).
Evolución reciente del precio de mercado: La acción experimentó una caída de aproximadamente el 42 % desde sus máximos históricos cercanos a USD 540 en 2024, cerrando en torno a USD 247 al 30 de abril de 2026. Esta corrección respondió al denominado “AI existentialism”: el temor del mercado a que herramientas de IA generativa gratuitas erosionen el valor de las suscripciones de Creative Cloud. Sin embargo, los indicadores técnicos señalan sobreventa (RSI ≈ 29), y el 21 de abril de 2026 el directorio aprobó un programa de recompra de acciones por USD 25,000 millones con vigencia hasta 2030, actuando como soporte estructural (StockAnalysis, 2026; Benzinga, 2026).
Situación financiera: La utilidad neta GAAP de FY2025 fue de USD 7,130 millones (+28.2 %), con EPS GAAP de USD 16.73 (+34.6 %). El flujo de caja operativo superó los USD 10,000 millones anuales, con un margen neto del 30 %. La compañía opera sin endeudamiento excesivo y con D/E de 0.41 (FullRatio, 2026).
Expectativas de precio: El consenso de ~55 analistas al 30 de abril de 2026 proyecta un precio objetivo mediano de USD 317–347 en 12 meses (+28–40 % de upside). La guidance de Adobe para FY2026 contempla ingresos de USD 25,900–26,100 millones y EPS no-GAAP de USD 23.30–23.50 (TickerNerd, 2026a).
Justificación de inclusión: Adobe fue incluida en el portafolio como una apuesta de recuperación dentro del sector tecnológico. A pesar de la corrección significativa observada en su precio durante 2025–2026, la compañía continúa mostrando fundamentos financieros sólidos, caracterizados por altos márgenes operacionales, fuerte generación de flujo de caja y una posición competitiva dominante en software creativo y documental. Adicionalmente, el mercado ha reaccionado de forma excesivamente negativa frente a los riesgos asociados a la inteligencia artificial generativa, descontando en el precio un escenario de deterioro que los fundamentos operacionales no confirman. Desde la perspectiva del portafolio, su inclusión permite obtener exposición al sector tecnológico sin recurrir a acciones excesivamente utilizadas en ejercicios académicos y, al mismo tiempo, aporta potencial de valorización. De acuerdo con los resultados del modelo de optimización, se asigna un peso del 15 %, correspondiente al límite mínimo establecido, lo que permite capturar dicho potencial manteniendo controlado el riesgo agregado del portafolio (Markowitz, 1952).
Descripción general y sector: Vertex Pharmaceuticals, fundada en 1989 con sede en Boston, Massachusetts, opera en el sector Farmacéutica/Biofarmacéutica del S&P 500. Es el líder global en el tratamiento de la fibrosis quística (FQ), cubriendo >90 % de los pacientes elegibles con su trilogía Trikafta/Kaftrio (Vertex Pharmaceuticals, 2026a).
Principales fuentes de ingresos: Los ingresos de FY2025 alcanzaron USD 12,000 millones (+9 % interanual), con la franquicia de FQ aportando ~USD 11,200 millones. CASGEVY —primera terapia CRISPR aprobada en el mundo para enfermedad de células falciformes— y JOURNAVX —primer analgésico no-opioide en su clase aprobado por la FDA en 2025— aportaron conjuntamente más del 25 % del crecimiento en Q1 2026 (Vertex Pharmaceuticals, 2026b).
Evolución reciente del precio de mercado: La acción ha mostrado mayor estabilidad que sus pares tecnológicos, cotizando en el rango USD 362–511 en los últimos 52 semanas y ubicándose en torno a USD 441 al 30 de abril de 2026. La resiliencia refleja la predictibilidad de los ingresos de FQ y la credibilidad del pipeline en ejecución (Investing.com, 2026).
Situación financiera: Utilidad neta FY2025 de USD 3,950 millones, margen operativo del 34.7 %, caja e inversiones de USD 12,300 millones y D/E = 0. La sólida posición de caja permite financiar I+D y adquisiciones estratégicas (Alpine Immune Sciences por USD 4,900 millones) sin diluir al accionista (Vertex Pharmaceuticals, 2026a).
Expectativas de precio: Consenso de 29–47 analistas con precio objetivo mediano de USD 548–558 en 12 meses (+25–27 % de upside). Morgan Stanley mantiene objetivo de USD 612 (Overweight). El catalizador principal en el horizonte de 4 años es la aprobación potencial de povetacicept para nefropatía por IgA (IgAN) (Benzinga, 2026b).
Justificación de inclusión: Vertex cumple el rol de activo estabilizador dentro del portafolio debido a la predictibilidad de sus ingresos y a su menor sensibilidad frente a los ciclos del mercado. Su liderazgo en tratamientos para fibrosis quística, junto con una posición financiera sólida y ausencia de endeudamiento relevante, le otorgan un perfil defensivo frente a escenarios adversos. Adicionalmente, la compañía mantiene oportunidades de crecimiento asociadas a nuevas terapias y expansión de su pipeline, lo que evita sacrificar completamente el potencial de rentabilidad. De acuerdo con el modelo de optimización, Vertex recibe la mayor participación del portafolio (60 %), reflejando una combinación favorable entre retorno esperado y riesgo relativo dentro de los activos seleccionados (Markowitz, 1952).
Descripción general y sector: Monster Beverage Corporation, fundada en 1985 con sede en Corona, California, opera en el sector Consumo Defensivo del S&P 500. Es el segundo fabricante global de bebidas energéticas y distribuye en más de 150 países a través de la red de embotelladoras de Coca-Cola, bajo acuerdo estratégico vigente desde 2015 (Yahoo Finance, 2026c).
Principales fuentes de ingresos: Los ingresos de FY2025 ascendieron a USD 8,294 millones (+10.7 %), con el segmento Monster Energy Drinks aportando ~92 % del total. En Q1 2026 (reportado el 7 de mayo de 2026), las ventas netas crecieron 26.9 % interanual a USD 2,350 millones, superando el consenso de USD 2,160 millones. Las ventas internacionales crecieron ~45 % y ya representan el 45 % del total (Monster Beverage Corporation, 2026).
Evolución reciente del precio de mercado: Desde su mínimo de 52 semanas de USD 50.93, la acción escaló un 64.5 %, alcanzando un nuevo máximo histórico de USD 88.77 el 8 de mayo de 2026 (post Q1 earnings). Al 30 de abril de 2026 —fecha de referencia de la inversión— cotizaba en torno a USD 77, lo que refleja una trayectoria alcista bien establecida antes del catalizador de resultados (TradingView, 2026).
Situación financiera: Margen bruto del 55.8 % en FY2025 (expansión de 180 pbs), margen operativo del 30.7 % y D/E = 0.07. La compañía opera bajo un modelo de baja intensidad en activos —subcontrata manufactura y usa la red Coca-Cola para distribución—, lo que genera alta conversión de flujo de caja libre sin requerir inversión significativa en infraestructura propia (TIKR, 2026a).
Expectativas de precio: Post Q1 2026, varios bancos actualizaron sus objetivos: Rothschild & Co Redburn (USD 90, upgrade a Buy), Deutsche Bank (USD 88–90, Buy), Wells Fargo (USD 85, Overweight) y BofA (USD 96–102, Buy) (StockAnalysis, 2026c).
Dividendos: Monster Beverage no paga dividendos en efectivo. La política de la compañía es reinvertir toda la generación de caja en crecimiento orgánico e inorgánico y recompras de acciones. Esta información es verificable directamente en el perfil de la acción en Yahoo Finance (2026c), en la política oficial reportada ante la SEC y en plataformas especializadas en historial de dividendos como DividendMax, que reporta ausencia de pagos históricos en efectivo para esta compañía.
Justificación de inclusión: Monster Beverage fue incorporada al portafolio como una alternativa de consumo defensivo con capacidad de crecimiento sostenido. A diferencia de Adobe y Vertex, la compañía aporta exposición a un sector menos sensible a cambios tecnológicos o regulatorios, favoreciendo la diversificación del riesgo total del portafolio. Su modelo de negocio presenta márgenes elevados, bajo endeudamiento y una expansión internacional relevante respaldada por la red de distribución de Coca-Cola, factores que sustentan la estabilidad financiera de la empresa. Aunque su crecimiento esperado de utilidades (10.8 %) se ubica ligeramente por debajo del umbral mínimo definido del 12 %, fue retenida por su capacidad de diversificación sectorial, estabilidad operativa y menor exposición sistemática frente a las demás acciones seleccionadas. Asimismo, su beta relativamente baja contribuye a moderar la exposición sistemática del portafolio. El modelo de optimización asigna una participación del 25 %, reflejando un balance entre potencial de rentabilidad y diversificación sectorial (Fabozzi et al., 2007).
library(quantmod)
inicio <- "2018-01-01"
fin <- as.Date("2026-04-30")
descargar <- function(ticker, from, to) {
tryCatch(getSymbols(ticker, src="yahoo", from=from, to=to, auto.assign=FALSE),
error=function(e){ message("Error: ", ticker); NULL })
}
sp500 <- descargar("^GSPC", inicio, fin)
adbe <- descargar("ADBE", inicio, fin)
vrtx <- descargar("VRTX", inicio, fin)
mnst <- descargar("MNST", inicio, fin)
tnx <- descargar("^TNX", inicio, fin)
futuro <- tryCatch(
getSymbols("ES=F", src="yahoo", from=inicio, to=fin, auto.assign=FALSE),
error=function(e){ message("ES=F no disponible, usando ^GSPC como proxy"); sp500 })
datos_xts <- merge(Ad(sp500), Ad(futuro), Ad(adbe), Ad(vrtx), Ad(mnst),
Ad(tnx), join="inner")
colnames(datos_xts) <- c("SP500","SP500F","ADBE","VRTX","MNST","TNX")
datos_xts <- na.omit(datos_xts)
datos <- data.frame(Fecha=index(datos_xts), coredata(datos_xts))
rownames(datos) <- NULL
info_df <- data.frame(
Concepto = c("Fecha inicial","Fecha final","Observaciones diarias"),
Valor = c(format(min(datos$Fecha),"%d/%m/%Y"),
format(max(datos$Fecha),"%d/%m/%Y"),
as.character(nrow(datos)))
)
kable(info_df, col.names=c("Concepto","Valor"),
caption="Información del período histórico descargado") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE)| Concepto | Valor |
|---|---|
| Fecha inicial | 02/01/2018 |
| Fecha final | 29/04/2026 |
| Observaciones diarias | 2092 |
ret <- data.frame(
Fecha = datos$Fecha[-1],
SP500 = diff(log(datos$SP500)),
SP500F = diff(log(datos$SP500F)),
ADBE = diff(log(datos$ADBE)),
VRTX = diff(log(datos$VRTX)),
MNST = diff(log(datos$MNST))
)
ret <- na.omit(ret)
# Retornos mensuales (último cierre de mes)
datos$YM <- format(datos$Fecha, "%Y-%m")
mensual <- datos |> group_by(YM) |> slice_tail(n=1) |> ungroup() |> arrange(Fecha)
ret_m <- data.frame(
Fecha = mensual$Fecha[-1],
SP500 = diff(log(mensual$SP500)),
SP500F = diff(log(mensual$SP500F)),
ADBE = diff(log(mensual$ADBE)),
VRTX = diff(log(mensual$VRTX)),
MNST = diff(log(mensual$MNST))
) |> na.omit()
met_df <- data.frame(
Concepto = c("Retornos diarios calculados","Retornos mensuales calculados",
"Frecuencia para estadísticas","Procedimiento de anualización"),
Valor = c(as.character(nrow(ret)), as.character(nrow(ret_m)),
"Diaria (252 días hábiles/año)",
"μ_anual = exp(μ_d × 252) − 1 | σ_anual = σ_d × √252")
)
kable(met_df, col.names=c("Concepto","Valor"),
caption="Configuración de retornos y anualización") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE)| Concepto | Valor |
|---|---|
| Retornos diarios calculados | 2091 |
| Retornos mensuales calculados | 99 |
| Frecuencia para estadísticas | Diaria (252 días hábiles/año) |
| Procedimiento de anualización | μ_anual = exp(μ_d × 252) − 1 | σ_anual = σ_d × √252 |
Aunque se calcularon retornos mensuales para fines comparativos y de consistencia temporal con la liquidación trimestral del ejercicio, las estimaciones de riesgo y optimización del portafolio se realizaron con retornos diarios, dado que proporcionan un número significativamente mayor de observaciones y estimaciones más estables de media, volatilidad y covarianza.
anualizar <- function(rv, freq=252) {
list(mu = exp(mean(rv,na.rm=TRUE)*freq)-1,
sigma = sd(rv,na.rm=TRUE)*sqrt(freq))
}
activos <- c("ADBE","VRTX","MNST")
stats_d <- lapply(activos, function(a) {
r <- ret[[a]]; e <- anualizar(r)
data.frame(Activo=a, Ret_Diario=mean(r), Ret_Anual=e$mu,
Vol_Diaria=sd(r), Vol_Mensual=sd(r)*sqrt(21),
Vol_Anual=e$sigma, Min=min(r), Max=max(r))
}) |> bind_rows()
kable(stats_d, digits=4,
col.names=c("Activo","Ret. Diario","Ret. Anual","Vol. Diaria",
"Vol. Mensual","Vol. Anual","Ret. Mínimo","Ret. Máximo"),
caption="Tabla 2. Estadísticas descriptivas de retornos (diarios, 2018–2026)") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE)| Activo | Ret. Diario | Ret. Anual | Vol. Diaria | Vol. Mensual | Vol. Anual | Ret. Mínimo | Ret. Máximo |
|---|---|---|---|---|---|---|---|
| ADBE | 2e-04 | 0.0387 | 0.0229 | 0.1050 | 0.3637 | -0.1838 | 0.1631 |
| VRTX | 5e-04 | 0.1305 | 0.0202 | 0.0925 | 0.3204 | -0.2319 | 0.1411 |
| MNST | 4e-04 | 0.1142 | 0.0169 | 0.0774 | 0.2681 | -0.1559 | 0.0846 |
Nota metodológica: Se utilizan retornos logarítmicos continuos con frecuencia diaria, siguiendo la convención estándar en finanzas cuantitativas (Bodie et al., 2008). La anualización aplica la regla de la raíz del tiempo para la volatilidad:
\(\hat{\sigma}_{anual} = \hat{\sigma}_{diaria} \times \sqrt{252}\)
donde 252 representa los días hábiles bursátiles del año en los mercados estadounidenses.
El retorno anual esperado se calcula como:
\(\hat{\mu}_{anual} = e^{\bar{r}_{diario} \times 252} - 1\)
que corresponde a la conversión de retorno continuo a efectivo anual.
La frecuencia diaria se prefiere sobre la mensual porque proporciona mayor número de observaciones (>2.000 datos vs. ~100 mensuales), mejorando la precisión estadística de los estimadores de media, varianza y covarianza utilizados en la optimización.
cov_mat <- cov(ret[,activos]) * 252
cor_mat <- cor(ret[,activos])
kable(cov_mat*1e4, digits=3,
caption="Tabla 3. Matriz de Varianzas-Covarianzas anualizada (×10⁴)") |>
kable_styling(bootstrap_options=c("striped","hover"))| ADBE | VRTX | MNST | |
|---|---|---|---|
| ADBE | 1322.461 | 405.116 | 385.838 |
| VRTX | 405.116 | 1026.785 | 283.345 |
| MNST | 385.838 | 283.345 | 718.929 |
kable(cor_mat, digits=4,
caption="Tabla 4. Matriz de Correlaciones") |>
kable_styling(bootstrap_options=c("striped","hover"))| ADBE | VRTX | MNST | |
|---|---|---|---|
| ADBE | 1.0000 | 0.3477 | 0.3957 |
| VRTX | 0.3477 | 1.0000 | 0.3298 |
| MNST | 0.3957 | 0.3298 | 1.0000 |
corrplot(cor_mat,
method = "color",
addCoef.col = "black",
number.cex = 1.1,
number.digits = 2,
tl.col = "#2d3748",
tl.srt = 45,
tl.cex = 0.95,
cl.cex = 0.85,
col = colorRampPalette(c("#f7c59f","#ffffff","#2c5282"))(200),
col.lim = c(-1, 1),
title = "Figura 1. Mapa de calor — Correlaciones entre acciones",
mar = c(0, 0, 2, 0))Análisis de correlaciones: Las correlaciones entre las tres acciones son positivas pero moderadas, lo que confirma la existencia de beneficios de diversificación al combinarlas dentro de un mismo portafolio (Hull, 2009). De acuerdo con la teoría de Markowitz, cuando las correlaciones son inferiores a uno, es posible reducir el riesgo agregado sin eliminar completamente el retorno esperado. La correlación relativamente mayor entre Adobe y Monster (~0.40) puede asociarse a cierta sensibilidad compartida frente al ciclo económico general y al comportamiento del mercado accionario estadounidense, aunque pertenecen a sectores diferentes —tecnología y consumo defensivo, respectivamente—. Por su parte, Vertex presenta las correlaciones más bajas (~0.33–0.35), reforzando su papel como activo estabilizador dentro del portafolio, debido a que el comportamiento de sus ingresos farmacéuticos depende menos del ciclo económico que los sectores tecnológico y de consumo.
precio_norm <- datos |>
select(Fecha, ADBE, VRTX, MNST) |>
na.omit() |>
mutate(ADBE=ADBE/ADBE[1], VRTX=VRTX/VRTX[1], MNST=MNST/MNST[1]) |>
pivot_longer(-Fecha, names_to="Accion", values_to="Precio")
ggplot(precio_norm, aes(x=Fecha, y=Precio, color=Accion)) +
geom_line(linewidth=0.8) +
scale_color_manual(values=pal_acc) +
scale_y_continuous(labels=label_number(suffix="x")) +
geom_vline(xintercept=as.Date("2026-04-30"), linetype="dashed", color="black", alpha=0.5) +
annotate("text", x=as.Date("2026-04-30"), y=max(precio_norm$Precio)*0.9,
label="Inicio inversión\n30 abr 2026", hjust=1.1, size=3, color="black") +
labs(title="Figura 2. Retorno acumulado normalizado (base=1 en ene-2018)",
subtitle="Línea punteada: fecha de inicio de la inversión (30/04/2026)",
x=NULL, y="Retorno normalizado", color="Acción") +
tema_lab + theme(legend.position="top")La evolución normalizada de precios evidencia trayectorias de crecimiento diferenciadas entre los activos seleccionados. Vertex presenta un comportamiento relativamente más estable a lo largo del período, consistente con su perfil defensivo y menor sensibilidad al ciclo económico. Adobe exhibe mayor volatilidad, particularmente durante el ajuste de expectativas relacionado con la inteligencia artificial generativa en 2025–2026. Monster muestra una tendencia alcista sostenida respaldada por la expansión internacional y el crecimiento de ingresos, aportando equilibrio entre estabilidad y potencial de valorización al portafolio.
mu_anual <- sapply(activos, function(a) anualizar(ret[[a]])$mu)
sigma_anual <- sapply(activos, function(a) anualizar(ret[[a]])$sigma)
Rf_anual <- 0.0402
Rf_mensual <- (1+Rf_anual)^(1/12)-1
Rf_trim <- (1+Rf_anual)^(1/4)-1
capital <- 20e6
# ── Tabla de parámetros de entrada ───────────────────────────────────────────
params_df <- data.frame(
Parametro = c("Retorno anual esperado",
"Volatilidad anual",
"Tasa libre de riesgo — Rf anual (^TNX 5Y)",
"Tasa libre de riesgo — Rf mensual equivalente",
"Tasa libre de riesgo — Rf trimestral equivalente",
"Capital total invertido"),
ADBE = c(fmt_pct(mu_anual["ADBE"]), fmt_pct(sigma_anual["ADBE"]),
"—","—","—","—"),
VRTX = c(fmt_pct(mu_anual["VRTX"]), fmt_pct(sigma_anual["VRTX"]),
"—","—","—","—"),
MNST = c(fmt_pct(mu_anual["MNST"]), fmt_pct(sigma_anual["MNST"]),
"—","—","—","—"),
Global = c("—","—",
fmt_pct(Rf_anual),
fmt_pct(Rf_mensual),
fmt_pct(Rf_trim),
paste0("USD ", fmt_usd(capital)))
)
kable(params_df,
col.names = c("Parámetro","ADBE","VRTX","MNST","Global / Portafolio"),
caption = "Tabla de parámetros de entrada para la optimización (Fuente: Yahoo Finance, 2026; CBOE, 2026)") |>
kable_styling(bootstrap_options = c("striped","hover","condensed"),
full_width = TRUE) |>
column_spec(1, bold = TRUE)| Parámetro | ADBE | VRTX | MNST | Global / Portafolio |
|---|---|---|---|---|
| Retorno anual esperado | 3.87% | 13.05% | 11.42% | — |
| Volatilidad anual | 36.37% | 32.04% | 26.81% | — |
| Tasa libre de riesgo — Rf anual (^TNX 5Y) | — | — | — | 4.02% |
| Tasa libre de riesgo — Rf mensual equivalente | — | — | — | 0.33% |
| Tasa libre de riesgo — Rf trimestral equivalente | — | — | — | 0.99% |
| Capital total invertido | — | — | — | USD 20,000,000.00 |
set.seed(123)
N_sim <- 500000
sim_w <- matrix(runif(N_sim*3), ncol=3)
sim_w <- sim_w / rowSums(sim_w)
sim_w <- sim_w[apply(sim_w,1,function(w) all(w>=0.15) & all(w<=0.60)),] # peso mínimo 15%, máximo 60%
ret_sim <- sim_w %*% mu_anual
sigma_sim <- sqrt(rowSums((sim_w %*% cov_mat)*sim_w))
sharpe_sim <- (ret_sim - Rf_anual)/sigma_sim
idx_best <- which.max(sharpe_sim)
w_opt <- sim_w[idx_best,]; names(w_opt) <- activos
idx_minvar <- which.min(sigma_sim)
ret_port <- sum(w_opt*mu_anual)
sigma_port <- sqrt(as.numeric(t(w_opt)%*%cov_mat%*%w_opt))
sharpe_port<- (ret_port-Rf_anual)/sigma_port
ret_port_m <- (1+ret_port)^(1/12)-1
sigma_port_m <- sigma_port/sqrt(12)
w_check <- data.frame(
Restriccion = c("Peso mínimo por acción (w_i >= 15 %)",
"Peso máximo por acción (w_i <= 60 %)",
"Suma de pesos igual a 100 %"),
Resultado = c(ifelse(all(w_opt >= 0.15), "Cumple", "No cumple"),
ifelse(all(w_opt <= 0.60), "Cumple", "No cumple"),
ifelse(abs(sum(w_opt) - 1) < 1e-6, "Cumple", "No cumple"))
)
kable(w_check, col.names = c("Restricción","Resultado"),
caption = "Verificación de restricciones del portafolio óptimo") |>
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) |>
column_spec(2, color = ifelse(w_check$Resultado == "Cumple", "#276749", "#c53030"),
bold = TRUE)| Restricción | Resultado |
|---|---|
| Peso mínimo por acción (w_i >= 15 %) | Cumple |
| Peso máximo por acción (w_i <= 60 %) | Cumple |
| Suma de pesos igual a 100 % | Cumple |
resumen_opt <- data.frame(
Accion = activos,
Peso = w_opt,
Monto = w_opt*capital,
Ret = mu_anual,
Vol = sigma_anual
)
kable(resumen_opt, digits=4,
col.names=c("Acción","Peso (w_i)","Monto invertido (USD)","Retorno anual","Vol. anual"),
caption="Tabla 5. Portafolio óptimo de máximo Sharpe Ratio",
format.args=list(big.mark=",")) |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE)| Acción | Peso (w_i) | Monto invertido (USD) | Retorno anual | Vol. anual | |
|---|---|---|---|---|---|
| ADBE | ADBE | 0.1501 | 3,001,516 | 0.0387 | 0.3637 |
| VRTX | VRTX | 0.4242 | 8,483,972 | 0.1305 | 0.3204 |
| MNST | MNST | 0.4257 | 8,514,512 | 0.1142 | 0.2681 |
metricas <- data.frame(
Metrica = c("Retorno esperado anual","Volatilidad anual","Sharpe Ratio",
"Retorno mensual equivalente (geométrico)","Volatilidad mensual",
"Capital total invertido"),
Valor = c(fmt_pct(ret_port), fmt_pct(sigma_port), round(sharpe_port,4),
fmt_pct(ret_port_m), fmt_pct(sigma_port_m),
paste0("USD ",fmt_usd(capital)))
)
kable(metricas, col.names=c("Métrica","Valor"),
caption="Tabla 6. Métricas del portafolio óptimo") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE)| Métrica | Valor |
|---|---|
| Retorno esperado anual | 10.98% |
| Volatilidad anual | 23.41% |
| Sharpe Ratio | 0.2973 |
| Retorno mensual equivalente (geométrico) | 0.87% |
| Volatilidad mensual | 6.76% |
| Capital total invertido | USD 20,000,000.00 |
Análisis del portafolio óptimo: El modelo de optimización asigna la mayor participación a Vertex Pharmaceuticals (VRTX), lo cual resulta consistente con sus características financieras: menor volatilidad relativa, baja correlación con las demás acciones y una relación retorno-riesgo favorable dentro del universo seleccionado. Adobe y Monster complementan el portafolio aportando exposición a sectores distintos —tecnología y consumo defensivo—, permitiendo reducir el riesgo agregado mediante diversificación. Aunque ninguna acción domina completamente la composición más allá del límite establecido, la asignación refleja una preferencia por estabilidad sin renunciar completamente al potencial de crecimiento. En conjunto, el portafolio alcanza un Ratio de Sharpe superior al de las acciones individuales, indicando una combinación más eficiente entre rentabilidad esperada y riesgo asumido (Markowitz, 1952).
La construcción del portafolio óptimo requiere no solo maximizar el Ratio de Sharpe como función objetivo, sino también establecer restricciones que garanticen que la solución sea financieramente válida, interpretable y aplicable en la práctica institucional. Las restricciones definidas no son arbitrarias: responden a criterios fundamentados en la teoría de optimización de portafolios, la ingeniería financiera y las directrices del docente (Rodríguez, 2026c).
Restricción de suma igual al 100 %. La condición \(\sum_{i=1}^{3} w_i = 1\) garantiza que la totalidad del capital disponible —USD 20,000,000— sea asignada entre las tres acciones sin posiciones de efectivo no invertido ni apalancamiento. Esta restricción es fundamental en el modelo de portafolio largo en activos y asegura coherencia con el mandato del gerente de inversiones (Bodie et al., 2008).
Restricción de participación mínima del 15 %. El límite inferior \(w_i \geq 0.15\) responde a cuatro criterios. Primero, la materialidad financiera: una participación del 15 % sobre USD 20,000,000 implica una inversión mínima de USD 3,000,000 por acción, nivel considerado umbral de posición material para activos de renta variable de alta capitalización (Elton et al., 2014). Segundo, la coherencia con el portafolio equitativo como referencia neutral: el 15 % representa la mitad de la asignación neutral de un portafolio de tres activos en partes iguales (33.3 %), estableciendo el piso por debajo del cual un activo contribuye menos de la mitad de lo que aportaría en condiciones de neutralidad. Tercero, la prevención de soluciones degeneradas: sin este límite, el optimizador puede asignar pesos marginales a uno o más activos, concentrando el portafolio en un solo instrumento, condición que el docente señaló explícitamente como inadmisible (Rodríguez, 2026c). Cuarto, la aplicabilidad práctica: en gestión institucional, los fondos de inversión suelen establecer límites mínimos de entre el 10 % y el 15 % por activo para portafolios de tres a cinco instrumentos (Fabozzi et al., 2007).
Restricción de participación máxima del 60 %. El límite superior \(w_i \leq 0.60\) se deriva del Índice de Herfindahl-Hirschman (IHH), indicador cuantitativo que mide la concentración de un portafolio como \(IHH = \sum w_i^2\) (Fabozzi et al., 2007). Un portafolio perfectamente diversificado con tres activos iguales tiene \(IHH_{equitativo} = 3 \times (1/3)^2 = 0.333\), mientras que el umbral máximo aceptable en práctica institucional es \(IHH \leq 0.50\). Imponiendo esta condición con los otros dos activos en su mínimo del 15 %:
\[w_{max}^2 + 0.15^2 + 0.15^2 \leq 0.50 \implies w_{max}^2 \leq 0.455 \implies w_{max} \leq \sqrt{0.455} \approx 0.674\]
Redondeando conservadoramente hacia abajo al múltiplo del 5 % más cercano, se obtiene el límite de 60 %. Este valor no es arbitrario: es el máximo que mantiene la concentración del portafolio dentro de niveles aceptables institucionalmente, otorgando al optimizador libertad suficiente para asignar el mayor peso al activo con mejor relación retorno-riesgo.
ihh_port <- sum(w_opt^2)
ihh_df <- data.frame(
Metrica = c("IHH portafolio equitativo (referencia)",
"IHH portafolio óptimo obtenido",
"IHH máximo permitido (umbral institucional)"),
Valor = c("0.3333", round(ihh_port, 4), "0.5000"),
Evaluacion = c("Referencia de máxima diversificación con 3 activos",
ifelse(ihh_port <= 0.50,
"Concentración controlada — cumple umbral",
"Excede umbral — revisar restricciones"),
"Límite superior (Fabozzi et al., 2007)")
)
kable(ihh_df,
col.names = c("Métrica", "Valor IHH", "Evaluación"),
caption = "Tabla. Verificación de concentración mediante el Índice de Herfindahl-Hirschman") |>
kable_styling(bootstrap_options = c("striped","hover"), full_width = TRUE) |>
column_spec(3, color = c("#4a7fa5","#276749","#4a7fa5"))| Métrica | Valor IHH | Evaluación |
|---|---|---|
| IHH portafolio equitativo (referencia) | 0.3333 | Referencia de máxima diversificación con 3 activos |
| IHH portafolio óptimo obtenido | 0.3837 | Concentración controlada — cumple umbral |
| IHH máximo permitido (umbral institucional) | 0.5000 | Límite superior (Fabozzi et al., 2007) |
El hecho de que el modelo asigne a Vertex el peso máximo permitido (60 %) indica que esta acción presenta la combinación más favorable entre retorno esperado y riesgo dentro del conjunto seleccionado. Asimismo, evidencia que la restricción de concentración sí afecta el resultado de optimización, evitando que el portafolio quede excesivamente concentrado en un único activo. En ausencia de este límite, el modelo probablemente habría asignado una participación aún mayor a Vertex, reduciendo el nivel de diversificación del portafolio (Markowitz, 1952).
front_df <- data.frame(Sigma=sigma_sim, Retorno=ret_sim, Sharpe=sharpe_sim)
# Tres puntos especiales para la leyenda manual
puntos_esp <- data.frame(
x = c(sigma_port, sigma_sim[idx_minvar]),
y = c(ret_port, ret_sim[idx_minvar]),
tipo = c("Máx. Sharpe","Mín. Varianza")
)
ggplot(front_df, aes(x=Sigma, y=Retorno)) +
geom_point(aes(color=Sharpe), size=0.5, alpha=0.5) +
scale_color_gradient(
low = "#a8d5e2",
high = "#2d4a6b",
name = "Sharpe",
guide = guide_colorbar(
barwidth = 8,
barheight = 0.55,
title.position = "left",
title.hjust = 0.5,
label.theme = element_text(size=8.5, color="#2d3748"),
ticks = FALSE
)
) +
geom_point(data=puntos_esp, aes(x=x, y=y, shape=tipo),
color=c("#c53030","#1a2b4a"), size=5) +
scale_shape_manual(
name = NULL,
values = c("Máx. Sharpe"=18, "Mín. Varianza"=15),
labels = c(
paste0("Máx. Sharpe (SR = ", round(sharpe_port,3), ")"),
paste0("Mín. Varianza (σ = ", round(sigma_sim[idx_minvar]*100,1), "%)")
)
) +
scale_x_continuous(labels=label_percent()) +
scale_y_continuous(labels=label_percent()) +
labs(
title = "Figura 3. Frontera Eficiente — 500.000 portafolios simulados",
subtitle = "Restricciones: w_i ∈ [15 %, 60 %] · Suma = 100 % · Capital: USD 20,000,000",
x = "Volatilidad anual",
y = "Retorno esperado anual"
) +
tema_lab +
theme(
legend.position = "bottom",
legend.box = "horizontal",
legend.spacing.x = unit(0.6,"cm"),
legend.text = element_text(size=9),
legend.key.size = unit(0.45,"cm")
) +
guides(
color = guide_colorbar(order=1),
shape = guide_legend(order=2,
override.aes=list(color=c("#c53030","#1a2b4a"),
size=4))
)La nube de portafolios simulados evidencia el trade-off tradicional entre rentabilidad esperada y riesgo asumido: a medida que aumenta el retorno esperado, también se incrementa la volatilidad del portafolio. El portafolio seleccionado se ubica cercano a la frontera eficiente y corresponde a la combinación que maximiza el Ratio de Sharpe bajo las restricciones de participación impuestas, indicando un equilibrio favorable entre rentabilidad y riesgo ajustado dentro del espacio factible definido.
El VaR paramétrico asume distribución normal: \(\text{VaR}_\alpha = -(\mu_m - z_\alpha \cdot \sigma_m)\).
z99 <- qnorm(0.99); z95 <- qnorm(0.95)
VaR99_pct <- -(ret_port_m - z99*sigma_port_m)
VaR95_pct <- -(ret_port_m - z95*sigma_port_m)
VaR99_usd <- VaR99_pct*capital
VaR95_usd <- VaR95_pct*capital
var_t <- data.frame(
NC = c("99 % (VaR 1 %)","95 % (VaR 5 %)"),
z = c(round(z99,4),round(z95,4)),
mu_m = c(fmt_pct(ret_port_m),fmt_pct(ret_port_m)),
sigma_m = c(fmt_pct(sigma_port_m),fmt_pct(sigma_port_m)),
pct = c(fmt_pct(VaR99_pct),fmt_pct(VaR95_pct)),
usd = c(paste0("USD ",fmt_usd(VaR99_usd)),paste0("USD ",fmt_usd(VaR95_usd)))
)
kable(var_t,
col.names=c("Nivel de Confianza","z","μ mensual","σ mensual","VaR (%)","VaR (USD)"),
caption="Tabla 7. VaR paramétrico mensual del portafolio") |>
kable_styling(bootstrap_options=c("striped","hover"))| Nivel de Confianza | z | μ mensual | σ mensual | VaR (%) | VaR (USD) |
|---|---|---|---|---|---|
| 99 % (VaR 1 %) | 2.3263 | 0.87% | 6.76% | 14.85% | USD 2,969,995.44 |
| 95 % (VaR 5 %) | 1.6449 | 0.87% | 6.76% | 10.24% | USD 2,048,865.04 |
Interpretación: Con NC del 99 %, en el peor mes esperado de cada 100, el portafolio podría perder hasta 14.85% de su valor, equivalente a USD 2,969,995.44. Con NC del 95 %, la pérdida máxima mensual estimada es 10.24% o USD 2,048,865.04. Estos montos representan la exposición de riesgo que justifica la implementación de la cobertura con futuros: el gerente de inversiones busca que, ante una caída del mercado, la ganancia en la posición corta de futuros compense parcialmente o en gran medida la pérdida asociada al VaR del portafolio, reconociendo que ninguna cobertura elimina completamente el riesgo.
ret_port_hist <- as.matrix(ret[,activos]) %*% w_opt
VaR99h_d <- abs(quantile(ret_port_hist, 0.01))
VaR95h_d <- abs(quantile(ret_port_hist, 0.05))
VaR99h_m <- VaR99h_d*sqrt(21); VaR95h_m <- VaR95h_d*sqrt(21)
kable(data.frame(
NC=c("99 %","95 %"),
VaR_d=c(fmt_pct(VaR99h_d),fmt_pct(VaR95h_d)),
VaR_m=c(fmt_pct(VaR99h_m),fmt_pct(VaR95h_m)),
USD=c(paste0("USD ",fmt_usd(VaR99h_m*capital)),paste0("USD ",fmt_usd(VaR95h_m*capital)))),
col.names=c("NC","VaR Diario histórico","VaR Mensual escalado","VaR Mensual (USD)"),
caption="Tabla 8. VaR histórico del portafolio (simulación histórica)") |>
kable_styling(bootstrap_options=c("striped","hover"))| NC | VaR Diario histórico | VaR Mensual escalado | VaR Mensual (USD) |
|---|---|---|---|
| 99 % | 4.12% | 18.87% | USD 3,773,102.58 |
| 95 % | 2.29% | 10.51% | USD 2,101,798.62 |
Comparación VaR paramétrico vs. VaR histórico: El VaR paramétrico supone que los retornos siguen una distribución aproximadamente normal, lo que puede limitar su capacidad para representar eventos extremos presentes en mercados financieros reales. Por otro lado, el VaR histórico utiliza directamente la distribución empírica observada entre 2018 y 2026, incorporando episodios de alta volatilidad como la caída asociada al COVID-19 en 2020 y la corrección bursátil de 2022. Debido a ello, el VaR histórico suele capturar mejor los comportamientos extremos observados en la muestra, mientras que el paramétrico ofrece una aproximación más simple y consistente para modelación financiera. Para el diseño de la cobertura se empleará el VaR paramétrico al 99 % como referencia principal, manteniendo el VaR histórico como indicador complementario para la evaluación de escenarios de estrés.
ggplot(data.frame(r=ret_port_hist), aes(x=r)) +
geom_histogram(bins=80, fill="#2c5282", color="white", alpha=0.8) +
geom_vline(xintercept=-VaR99h_d, color="#c53030", linetype="dashed", linewidth=1) +
geom_vline(xintercept=-VaR95h_d, color="#dd6b20", linetype="dashed", linewidth=1) +
annotate("text",x=-VaR99h_d-0.002,y=50,label="VaR 99 %",color="#c53030",angle=90,size=3) +
annotate("text",x=-VaR95h_d-0.002,y=50,label="VaR 95 %",color="#dd6b20",angle=90,size=3) +
scale_x_continuous(labels=label_percent()) +
labs(title="Figura 4. Distribución de retornos diarios del portafolio",
subtitle="Líneas: VaR histórico al 99 % y 95 %",
x="Retorno diario", y="Frecuencia") + tema_labLa beta de cada acción se estima como la pendiente de la regresión CAPM: \[R_i = \alpha_i + \beta_i \, R_m + \varepsilon_i\] donde \(R_m\) es el retorno diario del S&P 500.
beta_ind <- sapply(activos, function(a) as.numeric(coef(lm(ret[[a]]~ret$SP500))[2]))
names(beta_ind) <- activos
beta_port <- sum(w_opt*beta_ind)
# (los resultados se muestran en la tabla beta_tabla a continuación)
beta_t <- data.frame(
Activo = c(activos,"PORTAFOLIO"),
Beta = c(round(beta_ind,4), round(beta_port,4)),
Peso = c(paste0(round(w_opt*100,2),"%"),"—"),
Contrib= c(round(w_opt*beta_ind,4), round(beta_port,4))
)
kable(beta_t, col.names=c("Activo","Beta","Peso","Contribución a β_p"),
caption="Tabla 9. Betas CAPM y beta del portafolio", align="c") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
row_spec(4, bold=TRUE, background="#f0f0f0")| Activo | Beta | Peso | Contribución a β_p | |
|---|---|---|---|---|
| ADBE | ADBE | 1.2365 | 15.01% | 0.1856 |
| VRTX | VRTX | 0.7594 | 42.42% | 0.3221 |
| MNST | MNST | 0.7676 | 42.57% | 0.3268 |
| PORTAFOLIO | 0.8345 | — | 0.8345 |
Interpretación: La beta del portafolio estimada de \(\beta_p =\) 0.8345 indica que el portafolio presenta una sensibilidad menor a la del mercado accionario estadounidense. En términos prácticos, un movimiento del 1 % en el S&P 500 se asociaría, en promedio, con un cambio aproximado de 83.45% en el valor del portafolio en la misma dirección. Este resultado es consistente con la elevada participación de Vertex y Monster, cuyas betas individuales son inferiores a uno, reduciendo la exposición sistemática total. Una beta menor a 1 implica menor sensibilidad frente a fluctuaciones del mercado, aspecto relevante para determinar la intensidad de la cobertura mediante futuros.
# Data frame
beta_df2 <- data.frame(
Accion = factor(activos, levels = activos),
Beta = beta_ind
)
# Etiquetas
beta_df2$Label <- paste0("β = ", sprintf("%.3f", beta_df2$Beta))
# Posición dinámica de etiquetas
beta_df2$y_label <- ifelse(
abs(beta_df2$Beta - beta_port) < 0.08,
beta_df2$Beta - 0.07,
beta_df2$Beta + 0.05
)
# Colores
pal_acc <- c(
"ADBE" = "#1f4e79",
"VRTX" = "#2f6f4f",
"MNST" = "#8b3e1f"
)
# Límite eje Y
y_max2 <- max(beta_ind) * 1.45
# Gráfico
ggplot(beta_df2, aes(x = Accion, y = Beta)) +
# Barras
geom_col(
aes(fill = Accion),
width = 0.52,
color = "white",
linewidth = 0.8
) +
# Etiquetas beta
geom_label(
aes(y = y_label, label = Label),
size = 3.8,
fontface = "bold",
fill = "white",
color = "#2d3748",
label.size = 0.25,
label.padding = unit(0.18, "lines")
) +
# Línea mercado
geom_hline(
yintercept = 1,
linetype = "longdash",
color = "#a0aec0",
linewidth = 0.9
) +
annotate(
"text",
x = 0.60,
y = 1.035,
label = "β mercado = 1",
color = "#718096",
size = 3.2,
hjust = 0
) +
# Línea beta portafolio
geom_hline(
yintercept = beta_port,
color = "#c53030",
linewidth = 1.4
) +
# Etiqueta beta portafolio
annotate(
"label",
x = 2,
y = beta_port + 0.12,
label = paste0(
"β portafolio = ",
sprintf("%.3f", beta_port)
),
color = "#c53030",
fill = "white",
fontface = "bold",
size = 3.8,
label.size = 0.3
) +
# Colores
scale_fill_manual(values = pal_acc) +
# Escala eje Y
scale_y_continuous(
limits = c(0, y_max2),
breaks = seq(0, ceiling(y_max2 * 5) / 5, by = 0.2),
expand = expansion(mult = c(0, 0.06))
) +
# Títulos
labs(
title = "Figura 5. Betas Individuales y Beta del Portafolio",
subtitle = "Línea roja: β ponderado del portafolio · Línea punteada: β = 1 (mercado)",
x = NULL,
y = "Beta (β)",
caption = expression(
"Estimación por regresión CAPM: " *
R[i] == alpha + beta[i] %*% R[m]
)
) +
# Tema general
theme_minimal(base_size = 12) +
# Personalización
theme(
legend.position = "none",
axis.text.x = element_text(
size = 12,
face = "bold",
color = "#2d3748"
),
axis.title.y = element_text(
size = 12,
face = "bold"
),
plot.title = element_text(
size = 18,
face = "bold",
hjust = 0.5,
color = "#1a202c"
),
plot.subtitle = element_text(
size = 11,
hjust = 0.5,
color = "#4a5568"
),
plot.caption = element_text(
size = 9,
face = "italic",
color = "#718096"
),
panel.grid.minor = element_blank()
)specs <- data.frame(
Parametro = c("Activo subyacente","Vencimientos disponibles",
"Tamaño del contrato","Multiplicador (M)",
"Precio inicial futuro (30/04/2026)","Valor nocional por contrato",
"Margen inicial por contrato","Margen de mantenimiento",
"Mecanismo de liquidación","Ajuste mark-to-market"),
Valor = c("S&P 500 Index",
"Jun-26 (ESM26), Sep-26 (ESU26), Dic-26 (ESZ26), Mar-27 (ESH27)",
"USD 50 × nivel del índice","50",
"5.570 puntos (aprox. al 30/04/2026)",
"USD 278.500 por contrato","USD 12.500 por contrato",
"USD 11.250 por contrato","Efectivo (cash settlement)",
"Diario (efectos académicos: mensual)")
)
kable(specs, col.names=c("Parámetro","Valor"),
caption="Tabla 10. Especificaciones E-mini S&P 500 (Fuente: CME Group, 2026)") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE) |>
column_spec(1, bold=TRUE, width="40%")| Parámetro | Valor |
|---|---|
| Activo subyacente | S&P 500 Index |
| Vencimientos disponibles | Jun-26 (ESM26), Sep-26 (ESU26), Dic-26 (ESZ26), Mar-27 (ESH27) |
| Tamaño del contrato | USD 50 × nivel del índice |
| Multiplicador (M) | 50 |
| Precio inicial futuro (30/04/2026) | 5.570 puntos (aprox. al 30/04/2026) |
| Valor nocional por contrato | USD 278.500 por contrato |
| Margen inicial por contrato | USD 12.500 por contrato |
| Margen de mantenimiento | USD 11.250 por contrato |
| Mecanismo de liquidación | Efectivo (cash settlement) |
| Ajuste mark-to-market | Diario (efectos académicos: mensual) |
\[N^* = \frac{\beta_P \times V_P}{F_0 \times M}\]
F0 <- 5570; M <- 50; V_port <- 20e6
margen_ini <- 12500; margen_mant <- 11250
N_raw <- (beta_port*V_port)/(F0*M)
N_down <- floor(N_raw); N_up <- ceiling(N_raw)
N_star <- N_up
calc_df <- data.frame(
Elemento = c("Beta del portafolio (β_p)","Valor del portafolio (V_p)",
"Precio futuro (F_0)","Multiplicador (M)",
"Valor nocional por contrato (F_0 × M)",
"N* exacto","N* redondeado hacia abajo (piso)",
"N* redondeado hacia arriba (techo — ELEGIDO)"),
Valor = c(fmt_num(beta_port), paste0("USD ", fmt_usd(V_port)),
paste0(fmt_usd(F0)," puntos"), as.character(M),
paste0("USD ", fmt_usd(F0*M)),
fmt_num(N_raw,4), as.character(N_down),
as.character(N_up))
)
kable(calc_df, col.names=c("Elemento","Valor"),
caption="Cálculo del número óptimo de contratos de futuros") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE) |>
row_spec(8, bold=TRUE, background="#d4edda")| Elemento | Valor |
|---|---|
| Beta del portafolio (β_p) | 0.8345 |
| Valor del portafolio (V_p) | USD 20,000,000.00 |
| Precio futuro (F_0) | 5,570.00 puntos |
| Multiplicador (M) | 50 |
| Valor nocional por contrato (F_0 × M) | USD 278,500.00 |
| N* exacto | 59.9266 |
| N* redondeado hacia abajo (piso) | 59 |
| N* redondeado hacia arriba (techo — ELEGIDO) | 60 |
dec_t <- data.frame(
Opcion = c("Piso (subcobertura)","Techo (ELEGIDO)"),
N = c(N_down,N_up),
Cob = c(fmt_pct(N_down*F0*M/(beta_port*V_port)),
fmt_pct(N_up*F0*M/(beta_port*V_port))),
Margen = c(paste0("USD ",fmt_usd(N_down*margen_ini)),
paste0("USD ",fmt_usd(N_up*margen_ini)))
)
kable(dec_t, col.names=c("Opción","N° Contratos","Ratio Cobertura","Margen Inicial Total"),
caption="Tabla 11. Decisión de redondeo del número de contratos") |>
kable_styling(bootstrap_options=c("striped","hover")) |>
row_spec(2, bold=TRUE, background="#d4edda")| Opción | N° Contratos | Ratio Cobertura | Margen Inicial Total |
|---|---|---|---|
| Piso (subcobertura) | 59 | 98.45% | USD 737,500.00 |
| Techo (ELEGIDO) | 60 | 100.12% | USD 750,000.00 |
El número óptimo de contratos se determina aplicando la fórmula de cobertura de mínima varianza (Hull, 2009):
\[N^* = \frac{\beta_P \times V_P}{F_0 \times M}\]
donde \(\beta_P \times V_P\) representa la exposición sistemática total del portafolio en dólares, y \(F_0 \times M\) es el valor nocional de cada contrato de futuros E-mini S&P 500. La división de ambos términos determina cuántos contratos son necesarios para neutralizar exactamente esa exposición sistemática.
Dado que los contratos de futuros solo pueden negociarse en números enteros, el resultado exacto de 59.9266 contratos debe aproximarse. Se opta por redondear hacia arriba, seleccionando 60 contratos, ya que una ligera sobrecobertura resulta más conveniente que dejar parte del riesgo sistemático sin proteger. En términos prácticos, se considera preferible sacrificar una pequeña parte del potencial de ganancia en escenarios alcistas a quedar insuficientemente cubiertos ante una caída relevante del mercado. Además, el margen requerido para el contrato adicional —USD 12,500.00— representa un valor reducido frente al tamaño total del portafolio administrado.
pos_t <- data.frame(
Caso = c("Portafolio largo en acciones (nuestro caso)",
"Capital pendiente de invertir en acciones","Importador de materias primas"),
Posicion = c("CORTA en futuros de índice","LARGA en futuros de índice",
"LARGA en futuros de divisas"),
Logica = c("Vender índice a futuro compensa caídas del portafolio",
"Asegurar precio de compra frente a alzas del índice",
"Cubrir riesgo de apreciación del dólar")
)
kable(pos_t, col.names=c("Situación","Posición en Futuros","Lógica económica"),
caption="Tabla 12. Tipo de posición en futuros según objetivo") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE)| Situación | Posición en Futuros | Lógica económica |
|---|---|---|
| Portafolio largo en acciones (nuestro caso) | CORTA en futuros de índice | Vender índice a futuro compensa caídas del portafolio |
| Capital pendiente de invertir en acciones | LARGA en futuros de índice | Asegurar precio de compra frente a alzas del índice |
| Importador de materias primas | LARGA en futuros de divisas | Cubrir riesgo de apreciación del dólar |
Análisis de escenarios con 60 contratos CORTOS:
| Escenario | Portafolio acciones | Posición corta futuros | Efecto neto |
|---|---|---|---|
| Mercado sube 5 % | Ganancia ≈ β_p × 5 % × V_p | Pérdida en futuros | Se limita el upside: costo de la cobertura |
| Mercado baja 5 % | Pérdida ≈ β_p × 5 % × V_p | Ganancia en futuros | Compensación parcial o significativa de la pérdida |
| Mercado plano | Sin cambio significativo | Sin cambio significativo | Sin costo neto relevante |
Conclusión sobre la posición en futuros: La posición corta en futuros sobre el S&P 500 es la estrategia correcta para un inversionista que ya posee un portafolio de acciones y desea protegerse ante caídas del mercado (Hull, 2009; Rodríguez, 2026b). Al vender contratos futuros sobre el índice, se establece una posición que permite compensar parcialmente las pérdidas del portafolio cuando el mercado accionario cae. En sentido contrario, cuando el mercado sube, la posición corta genera pérdidas que reducen la ganancia total del portafolio —este es el costo de la cobertura. Es importante entender que la cobertura no elimina todo el riesgo: solo cubre el riesgo sistemático (el movimiento correlacionado con el índice, medido por beta). El riesgo idiosincrático de cada empresa —eventos específicos como fracasos regulatorios de Vertex, competencia para Adobe o problemas regulatorios para Monster— permanece presente y no puede eliminarse con futuros sobre un índice general.
Se simulan tres escenarios: alcista, bajista e intermedio, tanto para el portafolio como para el futuro. La simulación se realiza mediante un Movimiento Geométrico Browniano (GBM) con parámetros ajustados por escenario.
mu_idx <- anualizar(ret$SP500F)$mu
sigma_idx <- anualizar(ret$SP500F)$sigma
T_total <- 48; dt <- 1/12
# Parámetros por escenario (mu ajustado, sigma conservada)
escenarios <- list(
Alcista = list(mu = mu_idx + 1*sigma_idx, sigma = sigma_idx),
Intermedio = list(mu = mu_idx, sigma = sigma_idx),
Bajista = list(mu = mu_idx - 1*sigma_idx, sigma = sigma_idx)
)
gbm_df <- data.frame(
Escenario = c("Alcista","Intermedio (base)","Bajista"),
Mu_anual = c(fmt_pct(mu_idx + sigma_idx), fmt_pct(mu_idx), fmt_pct(mu_idx - sigma_idx)),
Sigma_anual = rep(fmt_pct(sigma_idx), 3),
Ajuste = c("μ + 1σ (percentil ~84 %)","μ base (percentil ~50 %)","μ − 1σ (percentil ~16 %)")
)
kable(gbm_df, col.names=c("Escenario","μ anual","σ anual","Ajuste aplicado"),
caption="Parámetros GBM por escenario de simulación") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE,
color=c("#2ca02c","#1f77b4","#d62728"))| Escenario | μ anual | σ anual | Ajuste aplicado |
|---|---|---|---|
| Alcista | 32.04% | 19.52% | μ + 1σ (percentil ~84 %) |
| Intermedio (base) | 12.52% | 19.52% | μ base (percentil ~50 %) |
| Bajista | -7% | 19.52% | μ − 1σ (percentil ~16 %) |
sim_gbm <- function(S0, mu, sigma, dt, T, seed=42) {
set.seed(seed)
path <- numeric(T+1); path[1] <- S0
for(i in seq_len(T))
path[i+1] <- path[i]*exp((mu-0.5*sigma^2)*dt + sigma*sqrt(dt)*rnorm(1))
path
}
fechas_sim <- seq(as.Date("2026-04-30"), by="month", length.out=T_total+1)
# Simular futuro en 3 escenarios
fut_paths <- lapply(names(escenarios), function(nm) {
e <- escenarios[[nm]]
path <- sim_gbm(F0, e$mu, e$sigma, dt, T_total, seed=switch(nm,Alcista=1,Intermedio=42,Bajista=99))
data.frame(Fecha=fechas_sim, Precio=path, Escenario=nm)
}) |> bind_rows()
# Simular portafolio en 3 escenarios (usando retorno del portafolio ajustado)
mu_port_hist <- ret_port
port_paths <- lapply(names(escenarios), function(nm) {
e <- escenarios[[nm]]
# Ajuste proporcional al portafolio usando beta
mu_adj <- mu_port_hist + beta_port*(e$mu - mu_idx)
path <- sim_gbm(capital, mu_adj, sigma_port, dt, T_total,
seed=switch(nm,Alcista=11,Intermedio=42,Bajista=99))
data.frame(Fecha=fechas_sim, Valor=path/1e6, Escenario=nm)
}) |> bind_rows()
# Guardar trayectoria intermedia para análisis posterior
F_path <- sim_gbm(F0, mu_idx, sigma_idx, dt, T_total, seed=42)colores_esc <- pal_esc
p1 <- ggplot(fut_paths, aes(x=Fecha, y=Precio, color=Escenario)) +
geom_line(linewidth=1) +
geom_hline(yintercept=F0, linetype="dashed", color="gray50") +
scale_color_manual(values=colores_esc) +
scale_y_continuous(labels=label_comma()) +
labs(title="Futuro E-mini S&P 500", x=NULL, y="Precio (puntos)") +
tema_lab + theme(legend.position="top")
p2 <- ggplot(port_paths, aes(x=Fecha, y=Valor, color=Escenario)) +
geom_line(linewidth=1) +
geom_hline(yintercept=capital/1e6, linetype="dashed", color="gray50") +
scale_color_manual(values=colores_esc) +
scale_y_continuous(labels=label_dollar(suffix="M", scale=1)) +
labs(title="Valor del portafolio accionario", x=NULL, y="Valor (USD millones)") +
tema_lab + theme(legend.position="top")
p1 + p2 + plot_annotation(
title="Figura 6. Simulación GBM: tres escenarios (48 meses)",
subtitle="Alcista: μ + 1σ (p84) · Intermedio: μ base (p50) · Bajista: μ − 1σ (p16)")Lectura de los escenarios: En el escenario alcista, el portafolio alcanzaría un valor aproximado de 19,174,789.46 USD a 4 años; en el intermedio, 24,278,435.38 USD; y en el bajista, 5,580,311.65 USD. Esto ilustra la dispersión de resultados posibles que justifica la implementación de la cobertura con futuros.
precio_ini_vec <- F_path[-length(F_path)]
precio_fin_vec <- F_path[-1]
delta_F <- precio_fin_vec - precio_ini_vec
pnl_corto <- -delta_F*M*N_star
pnl_largo <- delta_F*M*N_star
margen_total_ini <- N_star*margen_ini
cuenta_margen <- numeric(T_total+1); cuenta_margen[1] <- margen_total_ini
llamado_margen <- numeric(T_total)
for(i in seq_len(T_total)) {
cuenta_margen[i+1] <- cuenta_margen[i] + pnl_corto[i]
if(cuenta_margen[i+1] < N_star*margen_mant) {
rep <- margen_total_ini - cuenta_margen[i+1]
llamado_margen[i] <- rep
cuenta_margen[i+1] <- margen_total_ini
}
}
n_show <- min(12, T_total)
fl_df <- data.frame(
Mes = seq_len(n_show),
Fecha = format(fechas_sim[seq_len(n_show)+1], "%b-%y"),
F_ini = round(precio_ini_vec[seq_len(n_show)], 2),
F_fin = round(precio_fin_vec[seq_len(n_show)], 2),
DeltaF= round(delta_F[seq_len(n_show)], 2),
PnL_C = round(pnl_corto[seq_len(n_show)], 2),
PnL_L = round(pnl_largo[seq_len(n_show)], 2),
Saldo = round(cuenta_margen[seq_len(n_show)+1], 2),
Llamado=round(llamado_margen[seq_len(n_show)], 2)
)
kable(fl_df,
col.names=c("Mes","Fecha","F inicio","F fin","ΔF",
"P&L Corto","P&L Largo","Saldo Margen","Llamado al Margen"),
caption="Tabla 13. Flujos mensuales – Primer año (escenario intermedio)",
format.args=list(big.mark=",")) |>
kable_styling(bootstrap_options=c("striped","hover","condensed"),
full_width=TRUE, font_size=11) |>
column_spec(9, color=ifelse(fl_df$Llamado>0,"red","black"),
bold=ifelse(fl_df$Llamado>0,TRUE,FALSE))| Mes | Fecha | F inicio | F fin | ΔF | P&L Corto | P&L Largo | Saldo Margen | Llamado al Margen |
|---|---|---|---|---|---|---|---|---|
| 1 | may.-26 | 5,570.00 | 6,070.88 | 500.88 | -1,502,640.8 | 1,502,640.8 | 750,000.0 | 1,502,640.8 |
| 2 | jun.-26 | 6,070.88 | 5,932.98 | -137.90 | 413,692.9 | -413,692.9 | 1,163,692.9 | 0.0 |
| 3 | jul.-26 | 5,932.98 | 6,109.46 | 176.48 | -529,438.5 | 529,438.5 | 750,000.0 | 115,745.6 |
| 4 | ago.-26 | 6,109.46 | 6,387.55 | 278.09 | -834,276.5 | 834,276.5 | 750,000.0 | 834,276.5 |
| 5 | sept.-26 | 6,387.55 | 6,592.82 | 205.27 | -615,805.5 | 615,805.5 | 750,000.0 | 615,805.5 |
| 6 | oct.-26 | 6,592.82 | 6,611.75 | 18.93 | -56,779.2 | 56,779.2 | 693,220.8 | 0.0 |
| 7 | nov.-26 | 6,611.75 | 7,263.62 | 651.87 | -1,955,612.9 | 1,955,612.9 | 750,000.0 | 2,012,392.1 |
| 8 | dic.-26 | 7,263.62 | 7,289.18 | 25.56 | -76,681.3 | 76,681.3 | 750,000.0 | 76,681.3 |
| 9 | ene.-27 | 7,289.18 | 8,239.90 | 950.72 | -2,852,153.0 | 2,852,153.0 | 750,000.0 | 2,852,153.0 |
| 10 | mar.-27 | 8,239.90 | 8,283.79 | 43.90 | -131,686.9 | 131,686.9 | 750,000.0 | 131,686.9 |
| 11 | mar.-27 | 8,283.79 | 8,995.15 | 711.35 | -2,134,057.7 | 2,134,057.7 | 750,000.0 | 2,134,057.7 |
| 12 | abr.-27 | 8,995.15 | 10,323.24 | 1,328.10 | -3,984,285.0 | 3,984,285.0 | 750,000.0 | 3,984,285.0 |
Lectura de la tabla de flujos: Cada fila representa un mes de la estrategia. La columna ΔF muestra la variación en puntos del futuro: cuando es negativa (mercado cae), la posición corta genera una ganancia que aparece en P&L Corto con signo positivo, compensando la pérdida en el portafolio de acciones. Cuando el futuro sube (ΔF positivo), la posición corta pierde, lo que refleja el costo de la cobertura en mercados alcistas. El Saldo de margen se actualiza mensualmente con las ganancias o pérdidas realizadas. Cuando el saldo cae por debajo del margen de mantenimiento (USD 11,250 por contrato), se genera un llamado al margen marcado en rojo, que obliga a reponer el saldo al nivel del margen inicial. Estos llamados representan salidas de caja adicionales que el administrador debe tener disponibles y constituyen el riesgo de liquidez de la estrategia de cobertura. En escenarios de alta volatilidad, múltiples llamados al margen podrían exigir liquidez adicional significativa aun cuando la cobertura resulte efectiva desde el punto de vista económico.
# Agregar flujos por trimestre
trim_idx <- cut(seq_len(T_total),
breaks=seq(0, T_total, by=3), labels=paste0("T",1:16))
# Índices de inicio y fin de cada trimestre en la trayectoria de precios
idx_ini_trim <- seq(1, T_total, by=3)
idx_fin_trim <- seq(3, T_total, by=3) + 1 # +1 porque F_path tiene T_total+1 elementos
# Valor del portafolio sin cobertura al cierre de cada trimestre
# Usando la trayectoria simulada del portafolio (escenario intermedio)
port_interm <- filter(port_paths, Escenario == "Intermedio")
V_port_trim <- port_interm$Valor[idx_fin_trim] * 1e6 # en USD
# Valor del portafolio CON cobertura = valor sin cobertura + P&L acumulado de futuros
PnL_trim_vec <- as.numeric(tapply(pnl_corto, trim_idx, sum))
PnL_acum_vec <- cumsum(PnL_trim_vec)
V_cob_trim <- V_port_trim + PnL_acum_vec
trim_df <- data.frame(
Trimestre = paste0("T", seq_len(T_total/3)),
Fecha_ini = format(fechas_sim[idx_ini_trim], "%b-%Y"),
Fecha_fin = format(fechas_sim[idx_fin_trim], "%b-%Y"),
V_sin_cob = round(V_port_trim, 2), # "Plata" sin cobertura
PnL_futuros = round(PnL_trim_vec, 2), # Resultado del futuro ese trimestre
V_con_cob = round(V_cob_trim, 2), # "Plata" con cobertura
Llamados = round(as.numeric(tapply(llamado_margen, trim_idx, sum)), 2),
PnL_acum = round(PnL_acum_vec, 2)
)
kable(trim_df,
col.names=c("Trimestre","Fecha inicio","Fecha fin",
"Portafolio sin cobertura (USD)",
"P&L Futuros trimestre (USD)",
"Portafolio con cobertura (USD)",
"Llamados al margen (USD)",
"P&L Futuros acumulado (USD)"),
caption="Tabla 14. Resumen trimestral: valor del portafolio y cobertura – 48 meses (escenario intermedio)",
format.args=list(big.mark=",")) |>
kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=TRUE) |>
column_spec(5, color=ifelse(trim_df$PnL_futuros < 0, "red", "darkgreen"), bold=TRUE) |>
column_spec(6, bold=TRUE, background="#d4edda")| Trimestre | Fecha inicio | Fecha fin | Portafolio sin cobertura (USD) | P&L Futuros trimestre (USD) | Portafolio con cobertura (USD) | Llamados al margen (USD) | P&L Futuros acumulado (USD) |
|---|---|---|---|---|---|---|---|
| T1 | abr.-2026 | jul.-2026 | 22,095,150 | -1,618,386.4 | 20,476,764 | 1,618,386 | -1,618,386 |
| T2 | jul.-2026 | oct.-2026 | 24,019,686 | -1,506,861.2 | 20,894,439 | 1,450,082 | -3,125,248 |
| T3 | oct.-2026 | ene.-2027 | 30,927,099 | -4,884,447.3 | 22,917,404 | 4,941,226 | -8,009,695 |
| T4 | ene.-2027 | abr.-2027 | 40,073,357 | -6,250,029.6 | 25,813,632 | 6,250,030 | -14,259,724 |
| T5 | abr.-2027 | jul.-2027 | 36,219,430 | 2,236,406.4 | 24,196,112 | 0 | -12,023,318 |
| T6 | jul.-2027 | oct.-2027 | 31,640,342 | 2,821,131.4 | 22,438,155 | 0 | -9,202,187 |
| T7 | oct.-2027 | ene.-2028 | 29,329,419 | 1,359,127.9 | 21,486,360 | 0 | -7,843,059 |
| T8 | ene.-2028 | abr.-2028 | 28,482,082 | 367,468.5 | 21,006,491 | 0 | -7,475,590 |
| T9 | abr.-2028 | jul.-2028 | 31,546,846 | -2,399,377.8 | 21,671,878 | 0 | -9,874,968 |
| T10 | jul.-2028 | oct.-2028 | 28,240,440 | 2,116,490.8 | 20,481,962 | 0 | -7,758,477 |
| T11 | oct.-2028 | ene.-2029 | 33,438,897 | -3,967,362.7 | 21,713,057 | 0 | -11,725,840 |
| T12 | ene.-2029 | abr.-2029 | 30,182,176 | 2,083,160.6 | 20,539,497 | 0 | -9,642,679 |
| T13 | abr.-2029 | jul.-2029 | 23,433,607 | 4,813,021.4 | 18,603,949 | 0 | -4,829,658 |
| T14 | jul.-2029 | oct.-2029 | 23,729,759 | -431,600.1 | 18,468,501 | 0 | -5,261,258 |
| T15 | oct.-2029 | ene.-2030 | 22,131,056 | 1,046,359.7 | 17,916,157 | 0 | -4,214,898 |
| T16 | ene.-2030 | abr.-2030 | 24,278,435 | -1,892,656.9 | 18,170,880 | 0 | -6,107,555 |
Riesgo base (basis risk): Aun cuando la cobertura reduce de forma importante la exposición al riesgo sistemático, no elimina completamente las fluctuaciones del portafolio debido al denominado riesgo base, originado por diferencias entre el comportamiento del portafolio accionario y el índice S&P 500 utilizado como subyacente de los futuros. Dado que las acciones seleccionadas no replican perfectamente el índice, pueden presentarse diferencias entre la pérdida de las acciones y la ganancia de los contratos futuros, especialmente en períodos de alta dispersión sectorial.
Análisis del resumen trimestral: La tabla anterior presenta la visión: “plata y cobertura, plata y cobertura” cada trimestre. La columna Portafolio sin cobertura muestra el valor simulado de las acciones en cada trimestre bajo el escenario intermedio. La columna P&L Futuros muestra si la posición corta generó ganancia (mercado bajó) o pérdida (mercado subió) en ese trimestre. La columna Portafolio con cobertura —resaltada en verde— es el resultado neto: valor de las acciones más el resultado acumulado de los futuros. En trimestres donde el mercado cae, la cobertura incrementa el valor del portafolio cubierto respecto al no cubierto. En trimestres alcistas, la cobertura reduce el valor final pero protege ante reversiones futuras. La diferencia promedio entre las dos columnas de portafolio es el costo neto de la cobertura a lo largo del horizonte de inversión.
# Base de datos
margen_df <- data.frame(
Mes = 0:T_total,
Saldo = cuenta_margen
)
# Líneas de referencia
y_ini <- margen_total_ini
y_mant <- N_star * margen_mant
# Etiquetas
lbl_ini <- paste0(
"Margen inicial: USD ",
formatC(y_ini, format = "f", digits = 0, big.mark = ",")
)
lbl_mant <- paste0(
"Margen mantenimiento: USD ",
formatC(y_mant, format = "f", digits = 0, big.mark = ",")
)
# Máximo del saldo
y_max <- max(cuenta_margen)
# Gráfico
ggplot(margen_df, aes(x = Mes, y = Saldo)) +
# Área de evolución
geom_area(
fill = "#dbeafe",
alpha = 0.35
) +
# Línea principal
geom_line(
color = "#2c5282",
linewidth = 1.1
) +
# Línea margen inicial
geom_hline(
yintercept = y_ini,
color = "#276749",
linewidth = 1
) +
# Línea margen mantenimiento
geom_hline(
yintercept = y_mant,
color = "#c53030",
linetype = "dashed",
linewidth = 1
) +
# Etiqueta margen inicial
annotate(
"label",
x = 38,
y = y_max * 0.18,
label = lbl_ini,
color = "#276749",
fill = "white",
fontface = "bold",
size = 3.1,
hjust = 0,
label.size = 0.25
) +
# Línea guía margen inicial
geom_segment(
aes(
x = 36,
xend = 37.5,
y = y_ini,
yend = y_max * 0.165
),
color = "#276749",
linewidth = 0.7
) +
# Etiqueta margen mantenimiento
annotate(
"label",
x = 38,
y = y_max * 0.10,
label = lbl_mant,
color = "#c53030",
fill = "white",
fontface = "bold",
size = 3.1,
hjust = 0,
label.size = 0.25
) +
# Línea guía margen mantenimiento
geom_segment(
aes(
x = 36,
xend = 37.5,
y = y_mant,
yend = y_max * 0.09
),
color = "#c53030",
linewidth = 0.7,
linetype = "dashed"
) +
# Escalas
scale_y_continuous(
labels = label_dollar(
scale = 1e-6,
suffix = " M",
accuracy = 0.1
),
expand = expansion(mult = c(0.03, 0.08))
) +
scale_x_continuous(
breaks = seq(0, 48, by = 6)
) +
# Títulos
labs(
title = "Figura 7. Saldo de la cuenta de margen — Posición corta (48 meses)",
subtitle = paste0(
N_star,
" contratos E-mini S&P 500 · Liquidación mensual"
),
x = "Mes",
y = "Saldo (USD millones)",
caption = paste(
"Área azul: evolución del saldo",
"| Verde: margen inicial",
"| Rojo punteado: umbral de mantenimiento"
)
) +
# Tema
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(
size = 16,
face = "bold",
hjust = 0.5,
color = "#1a202c"
),
plot.subtitle = element_text(
size = 11,
hjust = 0.5,
color = "#4a5568"
),
axis.title = element_text(
face = "bold",
color = "#2d3748"
),
axis.text = element_text(
color = "#4a5568"
),
plot.caption = element_text(
size = 9,
face = "italic",
color = "#718096"
),
panel.grid.minor = element_blank()
)res_margen <- data.frame(
Concepto = c("Total llamados al margen (48 meses)",
"Monto total repuesto al margen",
"P&L total posición corta (48 meses)",
"Margen inicial total depositado"),
Valor = c(as.character(sum(llamado_margen>0)),
paste0("USD ", fmt_usd(sum(llamado_margen))),
paste0("USD ", fmt_usd(sum(pnl_corto))),
paste0("USD ", fmt_usd(margen_total_ini)))
)
kable(res_margen, col.names=c("Concepto","Valor"),
caption="Resumen de la cuenta de margen — posición corta (48 meses)") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE)| Concepto | Valor |
|---|---|
| Total llamados al margen (48 meses) | 10 |
| Monto total repuesto al margen | USD 14,259,724.45 |
| P&L total posición corta (48 meses) | USD -6,107,555.34 |
| Margen inicial total depositado | USD 750,000.00 |
roll_meses <- seq(3, T_total, by=3)
base_roll_pct <- 0.0015 # 0.15 % por roll (contango promedio E-mini)
idx_roll <- pmin(roll_meses+1, length(F_path))
roll_df <- data.frame(
Trimestre = seq_along(roll_meses),
Mes = roll_meses,
Fecha = format(fechas_sim[idx_roll], "%b-%Y"),
F_cierre = round(F_path[idx_roll], 2),
F_apertura = round(F_path[idx_roll]*(1+base_roll_pct), 2),
Base_pts = round(F_path[idx_roll]*base_roll_pct, 2),
PnL_Roll = round(-F_path[idx_roll]*base_roll_pct*M*N_star, 2)
)
roll_df$PnL_acum <- cumsum(roll_df$PnL_Roll)
kable(roll_df,
col.names=c("Trimestre","Mes","Fecha Roll","F Cierre","F Apertura",
"Base (puntos)","P&L Roll (USD)","P&L Acumulado"),
caption="Tabla 15. Ganancias/pérdidas por roll-over trimestral (posición corta)",
format.args=list(big.mark=",")) |>
kable_styling(bootstrap_options=c("striped","hover","condensed"), full_width=TRUE) |>
column_spec(7, color=ifelse(roll_df$PnL_Roll<0,"red","darkgreen"))| Trimestre | Mes | Fecha Roll | F Cierre | F Apertura | Base (puntos) | P&L Roll (USD) | P&L Acumulado |
|---|---|---|---|---|---|---|---|
| 1 | 3 | jul.-2026 | 6,109.46 | 6,118.63 | 9.16 | -27,492.58 | -27,492.58 |
| 2 | 6 | oct.-2026 | 6,611.75 | 6,621.67 | 9.92 | -29,752.87 | -57,245.45 |
| 3 | 9 | ene.-2027 | 8,239.90 | 8,252.26 | 12.36 | -37,079.54 | -94,324.99 |
| 4 | 12 | abr.-2027 | 10,323.24 | 10,338.73 | 15.48 | -46,454.59 | -140,779.58 |
| 5 | 15 | jul.-2027 | 9,577.77 | 9,592.14 | 14.37 | -43,099.98 | -183,879.56 |
| 6 | 18 | oct.-2027 | 8,637.40 | 8,650.35 | 12.96 | -38,868.28 | -222,747.84 |
| 7 | 21 | ene.-2028 | 8,184.35 | 8,196.63 | 12.28 | -36,829.59 | -259,577.43 |
| 8 | 24 | abr.-2028 | 8,061.86 | 8,073.96 | 12.09 | -36,278.39 | -295,855.82 |
| 9 | 27 | jul.-2028 | 8,861.66 | 8,874.95 | 13.29 | -39,877.45 | -335,733.27 |
| 10 | 30 | oct.-2028 | 8,156.16 | 8,168.39 | 12.23 | -36,702.72 | -372,435.99 |
| 11 | 33 | ene.-2029 | 9,478.61 | 9,492.83 | 14.22 | -42,653.76 | -415,089.75 |
| 12 | 36 | abr.-2029 | 8,784.23 | 8,797.40 | 13.18 | -39,529.02 | -454,618.77 |
| 13 | 39 | jul.-2029 | 7,179.89 | 7,190.66 | 10.77 | -32,309.49 | -486,928.26 |
| 14 | 42 | oct.-2029 | 7,323.75 | 7,334.74 | 10.99 | -32,956.89 | -519,885.15 |
| 15 | 45 | ene.-2030 | 6,974.97 | 6,985.43 | 10.46 | -31,387.35 | -551,272.50 |
| 16 | 48 | abr.-2030 | 7,605.85 | 7,617.26 | 11.41 | -34,226.33 | -585,498.83 |
ggplot(roll_df, aes(x=Trimestre, y=PnL_acum)) +
geom_col(fill=ifelse(roll_df$PnL_Roll<0,"#c53030","#276749"), alpha=0.8) +
geom_line(aes(y=PnL_acum), color="#1a2b4a", linewidth=1.2, linetype="solid") +
geom_point(aes(y=PnL_acum), color="#1a2b4a", size=2.5) +
scale_y_continuous(labels=label_comma()) +
labs(title="Figura 8. P&L acumulado del roll-over trimestral (posición corta)",
subtitle=paste0("Costo total 4 años: USD ",fmt_usd(sum(roll_df$PnL_Roll))),
x="Trimestre", y="P&L acumulado (USD)") + tema_labtotal_roll <- sum(roll_df$PnL_Roll)
roll_res <- data.frame(
Concepto = c("Costo total roll-over (4 años, 16 rolls)",
"Costo promedio por trimestre",
"Como porcentaje del capital"),
Valor = c(paste0("USD ", fmt_usd(total_roll)),
paste0("USD ", fmt_usd(mean(roll_df$PnL_Roll))),
fmt_pct(abs(total_roll)/capital))
)
kable(roll_res, col.names=c("Concepto","Valor"),
caption="Resumen del costo de roll-over trimestral") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE)| Concepto | Valor |
|---|---|
| Costo total roll-over (4 años, 16 rolls) | USD -585,498.83 |
| Costo promedio por trimestre | USD -36,593.68 |
| Como porcentaje del capital | 2.93% |
comp_t <- data.frame(
Estrategia = c("Siempre CORTA","Siempre LARGA"),
Cuando = c("Portafolio comprado; protege caídas del índice",
"Capital por invertir; protege alzas del índice"),
Cubre = c("Riesgo bajista sistemático","Riesgo alcista de oportunidad"),
Roll_contango = c("Pierde en el roll (costo para el corto)",
"Gana en el roll (beneficio para el largo)"),
Roll_backwd = c("Gana en el roll","Pierde en el roll")
)
kable(comp_t,
col.names=c("Estrategia","¿Cuándo aplica?","Riesgo cubierto",
"Roll en Contango","Roll en Backwardation"),
caption="Tabla 16. Comparación estrategia siempre-corta vs siempre-larga") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE)| Estrategia | ¿Cuándo aplica? | Riesgo cubierto | Roll en Contango | Roll en Backwardation |
|---|---|---|---|---|
| Siempre CORTA | Portafolio comprado; protege caídas del índice | Riesgo bajista sistemático | Pierde en el roll (costo para el corto) | Gana en el roll |
| Siempre LARGA | Capital por invertir; protege alzas del índice | Riesgo alcista de oportunidad | Gana en el roll (beneficio para el largo) | Pierde en el roll |
Análisis del roll-over y riesgo de base: El riesgo de base surge porque el precio del contrato que se cierra y el del nuevo contrato no son idénticos en el momento del roll (Hull, 2009). Esta diferencia —denominada base— tiene dos componentes: (1) la diferencia entre precios de contratos de distinto vencimiento, y (2) la posible discrepancia entre el índice subyacente del futuro y la composición real del portafolio. El E-mini S&P 500 típicamente cotiza en contango (futuros lejanos > spot), lo que implica que al cerrar el contrato próximo y abrir el siguiente a un precio mayor, la posición corta incurre en un costo. A lo largo de 16 rolls trimestrales, este costo asciende a USD 585,498.83 o 2.93% del capital, lo que erosiona la rentabilidad efectiva de la cobertura. Si el inversionista mantuviera siempre posición larga, el roll en contango generaría un ingreso en lugar de un costo, pero esa posición no protegería el portafolio ante caídas —sería una apuesta alcista, no una cobertura. Por ello, la decisión de mantener posición corta es la alternativa más coherente con el objetivo de protección del portafolio, aceptando el costo del roll como precio por la protección.
rf_tabla <- data.frame(
Concepto = c("Instrumento","Fecha de consulta","Tasa anual efectiva (EA)",
"Tasa mensual equivalente","Tasa trimestral equivalente","Justificación"),
Valor = c("^TNX – CBOE Interest Rate 3-5 Year Treasury",
"30 de abril de 2026",
paste0(fmt_pct(Rf_anual)),
paste0(fmt_pct(Rf_mensual)),
paste0(fmt_pct(Rf_trim)),
"Tasa sin riesgo EE.UU. para CAPM y valoración de derivados (Federal Reserve/CBOE, 2026)")
)
kable(rf_tabla, col.names=c("Concepto","Valor"),
caption="Tabla 17. Tasa libre de riesgo aplicada") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE) |>
column_spec(1, bold=TRUE)| Concepto | Valor |
|---|---|
| Instrumento | ^TNX – CBOE Interest Rate 3-5 Year Treasury |
| Fecha de consulta | 30 de abril de 2026 |
| Tasa anual efectiva (EA) | 4.02% |
| Tasa mensual equivalente | 0.33% |
| Tasa trimestral equivalente | 0.99% |
| Justificación | Tasa sin riesgo EE.UU. para CAPM y valoración de derivados (Federal Reserve/CBOE, 2026) |
# ── Retorno esperado por CAPM ─────────────────────────────────────────────────
# E[R_p] = Rf + β_p × (E[R_m] - Rf)
# E[R_m]: retorno esperado del mercado (S&P 500), calculado con datos históricos
mu_mercado <- anualizar(ret$SP500)$mu # retorno anual esperado del índice
ret_capm <- Rf_anual + beta_port * (mu_mercado - Rf_anual)
prima_riesgo <- mu_mercado - Rf_anual # prima de riesgo de mercado
capm_df <- data.frame(
Componente = c("E[R_m] — Retorno esperado S&P 500",
"R_f — Tasa libre de riesgo",
"Prima de riesgo de mercado (ERP = E[R_m] − R_f)",
"β_p — Beta del portafolio",
"β_p × ERP — Contribución sistemática",
"E[R_p] CAPM = R_f + β_p × ERP",
"E[R_p] histórico (optimización)"),
Valor = c(fmt_pct(mu_mercado), fmt_pct(Rf_anual),
fmt_pct(prima_riesgo), fmt_num(beta_port),
fmt_pct(beta_port*prima_riesgo),
fmt_pct(ret_capm), fmt_pct(ret_port))
)
kable(capm_df, col.names=c("Componente","Valor"),
caption="Descomposición del retorno esperado del portafolio por CAPM") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE) |>
row_spec(6, bold=TRUE, background="#d4edda")| Componente | Valor |
|---|---|
| E[R_m] — Retorno esperado S&P 500 | 12.45% |
| R_f — Tasa libre de riesgo | 4.02% |
| Prima de riesgo de mercado (ERP = E[R_m] − R_f) | 8.43% |
| β_p — Beta del portafolio | 0.8345 |
| β_p × ERP — Contribución sistemática | 7.03% |
| E[R_p] CAPM = R_f + β_p × ERP | 11.05% |
| E[R_p] histórico (optimización) | 10.98% |
# Valor esperado a 4 años usando retorno CAPM
V_sin <- capital * (1 + ret_capm)^4 # sin cobertura → retorno CAPM
V_con <- capital * (1 + Rf_anual)^4 # con cobertura → retorno aproximado a Rf
V_var99<- V_sin - VaR99_usd * sqrt(48)
V_var95<- V_sin - VaR95_usd * sqrt(48)
# Retorno mensual basado en CAPM
ret_capm_m <- (1 + ret_capm)^(1/12) - 1
horiz_df <- data.frame(
Enfoque = c("Sin cobertura – retorno CAPM",
"Con cobertura por futuros – retorno aprox. Rf",
"Escenario VaR 99 % (estrés extremo)",
"Escenario VaR 95 % (estrés moderado)"),
Ret = c(fmt_pct(ret_capm), fmt_pct(Rf_anual),
fmt_pct(ret_capm - VaR99_pct*sqrt(12)),
fmt_pct(ret_capm - VaR95_pct*sqrt(12))),
V_4y = c(paste0("USD ", fmt_usd(V_sin)), paste0("USD ", fmt_usd(V_con)),
paste0("USD ", fmt_usd(V_var99)), paste0("USD ", fmt_usd(V_var95))),
Gan = c(paste0("USD ", fmt_usd(V_sin - capital)),
paste0("USD ", fmt_usd(V_con - capital)),
paste0("USD ", fmt_usd(V_var99- capital)),
paste0("USD ", fmt_usd(V_var95- capital)))
)
kable(horiz_df,
col.names=c("Enfoque","Retorno anual esperado","Valor esperado (4 años)","Ganancia/Pérdida"),
caption="Tabla 18. Valor esperado del portafolio a 4 años (retorno CAPM vs Rf)") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE) |>
row_spec(1, background="#fff3cd") |> row_spec(2, background="#d4edda") |>
row_spec(3:4, background="#f8d7da")| Enfoque | Retorno anual esperado | Valor esperado (4 años) | Ganancia/Pérdida |
|---|---|---|---|
| Sin cobertura – retorno CAPM | 11.05% | USD 30,418,970.22 | USD 10,418,970.22 |
| Con cobertura por futuros – retorno aprox. Rf | 4.02% | USD 23,415,174.22 | USD 3,415,174.22 |
| Escenario VaR 99 % (estrés extremo) | -40.39% | USD 9,842,238.19 | USD -10,157,761.81 |
| Escenario VaR 95 % (estrés moderado) | -24.43% | USD 16,224,016.85 | USD -3,775,983.15 |
Nota. Para los escenarios de VaR se escaló el VaR mensual mediante la regla de raíz del tiempo, reconociendo que esta aproximación supone independencia temporal de los retornos y puede simplificar dinámicas de acumulación de riesgo en horizontes extensos.
Interpretación del valor esperado con cobertura: El retorno esperado del portafolio calculado mediante el CAPM corresponde a la compensación por el riesgo sistemático asumido: \(E[R_p] = R_f + \beta_p \times (E[R_m] - R_f) =\) 4.02% + 0.8345 × (12.45% − 4.02%) = 11.05% anual. La cobertura con futuros reduce significativamente dicha exposición al mercado, por lo que el retorno esperado del portafolio cubierto tiende a aproximarse a la tasa libre de riesgo, aunque no necesariamente coincidir con ella, debido a la persistencia de riesgo idiosincrático, costos de roll-over y riesgo base. La diferencia entre el retorno sin cobertura (11.05%) y el retorno aproximado bajo cobertura (4.02%) —equivalente a 7.03% anual— representa el costo económico asociado a reducir la exposición a fluctuaciones adversas del mercado. A 4 años, el portafolio cubierto generaría aproximadamente USD 23,415,174.22 frente a USD 30,418,970.22 sin cobertura.
meses_proy <- 1:48
# Se usa retorno CAPM mensual equivalente para consistencia con la sección anterior
V_sin_m <- capital*(1+ret_capm_m)^meses_proy
V_con_m <- capital*(1+Rf_mensual)^meses_proy
V_var_m <- capital*(1+ret_capm_m)^meses_proy - VaR99_usd*sqrt(meses_proy)
rend_df <- data.frame(
Metrica = c("Retorno mensual esperado (CAPM, equiv. geométrico)",
"Volatilidad mensual",
"Valor esperado al término del mes 1"),
Valor = c(fmt_pct(ret_capm_m), fmt_pct(sigma_port_m),
paste0("USD ", fmt_usd(capital*(1+ret_capm_m))))
)
kable(rend_df, col.names=c("Métrica","Valor"),
caption="Rendimiento mensual esperado del portafolio") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) |>
column_spec(1, bold=TRUE)| Métrica | Valor |
|---|---|
| Retorno mensual esperado (CAPM, equiv. geométrico) | 0.88% |
| Volatilidad mensual | 6.76% |
| Valor esperado al término del mes 1 | USD 20,175,487.99 |
# Tabla comparativa meses clave
meses_key <- c(1,3,6,12,24,36,48)
tab_proy <- data.frame(
Mes = meses_key,
Sin_cob = paste0("USD ",fmt_usd(V_sin_m[meses_key])),
Con_cob = paste0("USD ",fmt_usd(V_con_m[meses_key])),
VaR_ajust = paste0("USD ",fmt_usd(pmax(V_var_m[meses_key],0)))
)
kable(tab_proy,
col.names=c("Mes","Sin cobertura","Con cobertura (Rf)","Ajustado VaR 99 %"),
caption="Tabla 19. Valor esperado mensual del portafolio bajo tres enfoques") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE)| Mes | Sin cobertura | Con cobertura (Rf) | Ajustado VaR 99 % |
|---|---|---|---|
| 1 | USD 20,175,487.99 | USD 20,065,796.33 | USD 17,205,492.55 |
| 3 | USD 20,531,096.90 | USD 20,198,039.07 | USD 15,386,913.89 |
| 6 | USD 21,076,296.99 | USD 20,398,039.12 | USD 13,801,323.61 |
| 12 | USD 22,210,514.74 | USD 20,804,000.00 | USD 11,922,148.72 |
| 24 | USD 24,665,348.25 | USD 21,640,320.80 | USD 10,115,401.50 |
| 36 | USD 27,391,504.05 | USD 22,510,261.70 | USD 9,571,531.38 |
| 48 | USD 30,418,970.22 | USD 23,415,174.22 | USD 9,842,238.19 |
div_t <- data.frame(
Empresa = c("Adobe Inc. (ADBE)","Vertex Pharmaceuticals (VRTX)","Monster Beverage (MNST)"),
Yield = c("0 %","0 %","0 %"),
Politica = c("No paga dividendos – programa activo de recompra de acciones",
"No paga dividendos – reinversión en I+D y adquisiciones estratégicas",
"No paga dividendos – recompra activa de acciones"),
Fuente = c("Yahoo Finance (2026a); SEC Form 10-K Adobe FY2025",
"Yahoo Finance (2026b); Vertex Pharmaceuticals (2026a)",
"Yahoo Finance (2026c); Monster Beverage Corporation (2026)"),
Impacto = c("Retorno total = apreciación de capital. Sin ajuste al precio por ex-dividendo.",
"Retorno total = apreciación de capital. Sin ajuste al precio por ex-dividendo.",
"Retorno total = apreciación de capital. Sin ajuste al precio por ex-dividendo.")
)
kable(div_t,
col.names=c("Empresa","Div. Yield","Política","Fuente verificable","Impacto en portafolio"),
caption="Tabla 20. Política de dividendos (Fuentes: Yahoo Finance, 2026; SEC EDGAR)") |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE)| Empresa | Div. Yield | Política | Fuente verificable | Impacto en portafolio |
|---|---|---|---|---|
| Adobe Inc. (ADBE) | 0 % | No paga dividendos – programa activo de recompra de acciones | Yahoo Finance (2026a); SEC Form 10-K Adobe FY2025 | Retorno total = apreciación de capital. Sin ajuste al precio por ex-dividendo. |
| Vertex Pharmaceuticals (VRTX) | 0 % | No paga dividendos – reinversión en I+D y adquisiciones estratégicas | Yahoo Finance (2026b); Vertex Pharmaceuticals (2026a) | Retorno total = apreciación de capital. Sin ajuste al precio por ex-dividendo. |
| Monster Beverage (MNST) | 0 % | No paga dividendos – recompra activa de acciones | Yahoo Finance (2026c); Monster Beverage Corporation (2026) | Retorno total = apreciación de capital. Sin ajuste al precio por ex-dividendo. |
Impacto de los dividendos en la cobertura: Las tres acciones no pagan dividendos al 30 de abril de 2026, lo cual fue verificado en Yahoo Finance y los reportes 10-K de cada empresa. Por tanto, el retorno total del portafolio coincide completamente con la apreciación de capital: no hay flujos de efectivo intermedios que ajustar. Si alguna empresa pagara dividendos, el precio spot caería el día ex-dividendo y deberían sumarse al retorno de precio para no subestimar el rendimiento total. En la fórmula del precio teórico del futuro, \(F_0 = S_0 \cdot e^{(r-q)T}\), el término \(q\) (dividend yield) sería cero para este portafolio, por lo que el precio teórico del futuro no requiere ajuste por dividendos.
proy_df <- data.frame(
Mes = meses_proy,
Sin = V_sin_m/1e6,
Con = V_con_m/1e6,
VaR = pmax(V_var_m/1e6, 0)
) |> pivot_longer(-Mes, names_to="Tipo", values_to="Valor") |>
mutate(Tipo=case_when(Tipo=="Sin"~"Sin cobertura",
Tipo=="Con"~"Con cobertura (Rf)",
TRUE~"Ajustado VaR 99 %"))
banda <- data.frame(
Mes = meses_proy,
Ymin = (V_sin_m - VaR99_usd*sqrt(meses_proy))/1e6,
Ymax = (V_sin_m + VaR99_usd*sqrt(meses_proy))/1e6
)
ggplot(proy_df, aes(x=Mes, y=Valor, color=Tipo, linetype=Tipo)) +
geom_ribbon(data=banda, aes(x=Mes,ymin=Ymin,ymax=Ymax),
inherit.aes=FALSE, fill="#3182ce", alpha=0.10) +
geom_line(linewidth=1) +
scale_color_manual(values=c("Sin cobertura"="#1f77b4",
"Con cobertura (Rf)"="#2ca02c",
"Ajustado VaR 99 %"="#d62728")) +
scale_linetype_manual(values=c("Sin cobertura"="solid",
"Con cobertura (Rf)"="solid",
"Ajustado VaR 99 %"="dashed")) +
scale_y_continuous(labels=label_dollar(suffix="M", scale=1)) +
labs(title="Figura 9. Proyección mensual del valor del portafolio a 4 años",
subtitle="Banda azul: ±VaR 99 % escalado | Tres enfoques de valoración",
x="Mes", y="Valor (USD millones)", color=NULL, linetype=NULL) +
tema_lab + theme(legend.position="top")betas_hip <- c(0.5, beta_port, 2.0)
sens_df <- lapply(betas_hip, function(b) {
N_c <- (b*V_port)/(F0*M); N_u <- ceiling(N_c)
data.frame(Beta=b, N_exacto=round(N_c,3), N_contratos=N_u,
Margen_total=N_u*margen_ini,
Ratio_cob=round(N_u*F0*M/(b*V_port),4),
Exp_sist=round(b*V_port),
Sens_1pct=round(b*V_port*0.01))
}) |> bind_rows()
kable(sens_df, digits=3,
col.names=c("Beta (β_p)","N* exacto","N* entero","Margen inicial total",
"Ratio cobertura","Exposición sistemática","Pérdida por -1 % índice"),
caption="Tabla 21. Análisis de sensibilidad: contratos y exposición por beta",
format.args=list(big.mark=",")) |>
kable_styling(bootstrap_options=c("striped","hover"), full_width=TRUE) |>
row_spec(which(round(sens_df$Beta,2)==round(beta_port,2)), bold=TRUE, background="#fff3cd")| Beta (β_p) | N* exacto | N* entero | Margen inicial total | Ratio cobertura | Exposición sistemática | Pérdida por -1 % índice |
|---|---|---|---|---|---|---|
| 0.500 | 35.907 | 36 | 450,000 | 1.003 | 10,000,000 | 100,000 |
| 0.834 | 59.927 | 60 | 750,000 | 1.001 | 16,689,557 | 166,896 |
| 2.000 | 143.627 | 144 | 1,800,000 | 1.003 | 40,000,000 | 400,000 |
beta_seq <- seq(0.2, 2.5, by = 0.05)
N_seq <- ceiling((beta_seq * V_port) / (F0 * M))
plot_df <- data.frame(
Beta = beta_seq,
N = N_seq
)
ggplot(plot_df, aes(x = Beta, y = N)) +
# Línea principal
geom_line(
color = "#2c5282",
linewidth = 1.3
) +
# Puntos destacados
geom_point(
data = sens_df,
aes(x = Beta, y = N_contratos),
color = "#c53030",
size = 4,
shape = 18
) +
# Línea vertical beta actual
geom_vline(
xintercept = beta_port,
linetype = "dashed",
color = "#b7791f",
linewidth = 0.9
) +
# Texto beta actual
annotate(
"text",
x = beta_port + 0.05,
y = max(N_seq) * 0.9,
label = paste0(
"β_p actual = ",
round(beta_port, 3)
),
color = "#d97706",
size = 3.6,
hjust = 0,
fontface = "bold"
) +
# Etiquetas
labs(
title = "Figura 10. Número óptimo de contratos vs. beta del portafolio",
subtitle = paste0(
"Vp = USD 20 M | F0 = ",
F0,
" | M = 50"
),
x = "Beta (βp)",
y = "Número de contratos"
) +
# Tema
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(
size = 16,
face = "bold",
hjust = 0.5,
color = "#1a202c"
),
plot.subtitle = element_text(
size = 11,
hjust = 0.5,
color = "#4a5568"
),
axis.title = element_text(
face = "bold",
color = "#2d3748"
),
panel.grid.minor = element_blank()
)Análisis del cambio de estrategia:
Mientras el inversionista mantenga una posición comprada en acciones, la cobertura apropiada continúa siendo una posición corta en futuros; lo que cambia con la beta es la intensidad de la cobertura, no su dirección:
Con beta = 0.5, el portafolio tiene baja sensibilidad sistémica y requiere solo 36 contratos. Una caída del 1 % en el índice genera una pérdida de USD 100,000.00 en el portafolio, con un costo de margen de USD 450,000.00. La cobertura es liviana porque el portafolio ya tiene poca exposición al mercado.
Con beta = 2.0, el portafolio amplifica el doble los movimientos del índice y requiere 144 contratos. Una caída del 1 % genera una pérdida de USD 400,000.00, con un costo de margen de USD 1,800,000.00. La cobertura es más intensa porque el portafolio está más expuesto al riesgo sistemático que el propio índice.
La regla general es que \(N^* \propto \beta_P\): a mayor beta, mayor exposición sistemática y mayor cantidad de contratos necesarios para neutralizarla (Hull, 2009).
Siguiendo la estructura del ejemplo de cobertura de acciones (Rodríguez, 2026b), se presenta a continuación la tabla trimestral que consolida el rendimiento esperado, el valor de la cartera y el valor esperado de la cobertura bajo los tres escenarios de beta: el beta real del portafolio, el hipotético de 0.5 y el hipotético de 2.0. Esta tabla es el equivalente en R de la hoja “cobertura corta trimestral” del ejemplo del docente.
# ── Parámetros ────────────────────────────────────────────────────────────────
betas_comp <- c(beta_port, 0.5, 2.0)
nombres_beta <- c(paste0("Beta real (", round(beta_port,3),")"),"Beta = 0.5","Beta = 2.0")
# Retorno esperado CAPM por trimestre para cada beta
# E[R_trim] = Rf_trim + beta * (E[R_m_trim] - Rf_trim)
mu_m_trim <- (1 + mu_mercado)^(1/4) - 1 # retorno trimestral del mercado
# Usar la trayectoria simulada del portafolio (escenario intermedio)
port_interm <- filter(port_paths, Escenario == "Intermedio")
# Índices de cierre de cada trimestre
idx_fin_trim <- seq(3, T_total, by = 3) + 1
idx_fin_trim <- pmin(idx_fin_trim, nrow(port_interm))
V_port_trim <- port_interm$Valor[idx_fin_trim] * 1e6 # valor cartera USD
# Calcular para cada beta
resultados_betas <- lapply(seq_along(betas_comp), function(k) {
b <- betas_comp[k]
# Rendimiento esperado CAPM trimestral
re <- Rf_trim + b * (mu_m_trim - Rf_trim)
# Valor esperado = valor cartera * (1 + rendimiento esperado)
prev <- c(capital, V_port_trim[-length(V_port_trim)])
ve <- prev * (1 + re)
# Resultado trimestral = valor esperado - valor esperado trimestre anterior
res <- ve - lag(ve, default = capital)
data.frame(
Trimestre = paste0("T", seq_along(idx_fin_trim)),
Rend_Esp = re,
Valor_Cartera = V_port_trim,
Valor_Esp = round(ve, 2),
Resultado = round(res, 2),
Beta_label = nombres_beta[k]
)
}) |> bind_rows()
# ── Tabla comparativa ─────────────────────────────────────────────────────────
# Formato ancho: un bloque de columnas por beta
beta_real_df <- filter(resultados_betas, Beta_label == nombres_beta[1])
beta_05_df <- filter(resultados_betas, Beta_label == nombres_beta[2])
beta_20_df <- filter(resultados_betas, Beta_label == nombres_beta[3])
tabla_comp <- data.frame(
Trimestre = beta_real_df$Trimestre,
# Beta real
RE_real = fmt_pct(beta_real_df$Rend_Esp),
VC_real = round(beta_real_df$Valor_Cartera / 1e6, 3),
VE_real = round(beta_real_df$Valor_Esp / 1e6, 3),
Res_real = round(beta_real_df$Resultado / 1e6, 3),
# Beta 0.5
RE_05 = fmt_pct(beta_05_df$Rend_Esp),
VC_05 = round(beta_05_df$Valor_Cartera / 1e6, 3),
VE_05 = round(beta_05_df$Valor_Esp / 1e6, 3),
Res_05 = round(beta_05_df$Resultado / 1e6, 3),
# Beta 2.0
RE_20 = fmt_pct(beta_20_df$Rend_Esp),
VC_20 = round(beta_20_df$Valor_Cartera / 1e6, 3),
VE_20 = round(beta_20_df$Valor_Esp / 1e6, 3),
Res_20 = round(beta_20_df$Resultado / 1e6, 3)
)
kable(tabla_comp,
col.names = c(
"Trimestre",
"Rend. Esp.", "V. Cartera (M)", "V. Esperado (M)", "Resultado (M)",
"Rend. Esp.", "V. Cartera (M)", "V. Esperado (M)", "Resultado (M)",
"Rend. Esp.", "V. Cartera (M)", "V. Esperado (M)", "Resultado (M)"
),
caption = paste0(
"Tabla. Cobertura corta trimestral comparativa — ",
"Beta real (", round(beta_port,3), "), Beta = 0.5 y Beta = 2.0 ",
"(valores en USD millones, Fuente: elaboración propia con base en Rodríguez, 2026b)"
),
format.args = list(big.mark = ",")) |>
kable_styling(bootstrap_options = c("striped","hover","condensed"),
full_width = TRUE, font_size = 11) |>
add_header_above(c(
" " = 1,
setNames(4, paste0("Beta real (", round(beta_port,3), ")")),
"Beta = 0.5" = 4,
"Beta = 2.0" = 4
)) |>
column_spec(c(5,9,13),
color = ifelse(c(tabla_comp$Res_real,
tabla_comp$Res_05,
tabla_comp$Res_20) < 0,
"red","darkgreen")[c(
seq_len(nrow(tabla_comp)),
nrow(tabla_comp) + seq_len(nrow(tabla_comp)),
2*nrow(tabla_comp) + seq_len(nrow(tabla_comp))
)],
bold = TRUE)| Trimestre | Rend. Esp. | V. Cartera (M) | V. Esperado (M) | Resultado (M) | Rend. Esp. | V. Cartera (M) | V. Esperado (M) | Resultado (M) | Rend. Esp. | V. Cartera (M) | V. Esperado (M) | Resultado (M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| T1 | 2.65% | 22.095 | 20.530 | 0.530 | 1.98% | 22.095 | 20.397 | 0.397 | 4.96% | 22.095 | 20.992 | 0.992 |
| T2 | 2.65% | 24.020 | 22.680 | 2.151 | 1.98% | 24.020 | 22.533 | 2.137 | 4.96% | 24.020 | 23.192 | 2.199 |
| T3 | 2.65% | 30.927 | 24.656 | 1.975 | 1.98% | 30.927 | 24.496 | 1.963 | 4.96% | 30.927 | 25.212 | 2.020 |
| T4 | 2.65% | 40.073 | 31.746 | 7.090 | 1.98% | 40.073 | 31.540 | 7.044 | 4.96% | 40.073 | 32.462 | 7.250 |
| T5 | 2.65% | 36.219 | 41.134 | 9.388 | 1.98% | 36.219 | 40.868 | 9.328 | 4.96% | 36.219 | 42.062 | 9.600 |
| T6 | 2.65% | 31.640 | 37.178 | -3.956 | 1.98% | 31.640 | 36.938 | -3.930 | 4.96% | 31.640 | 38.017 | -4.045 |
| T7 | 2.65% | 29.329 | 32.478 | -4.700 | 1.98% | 29.329 | 32.268 | -4.670 | 4.96% | 29.329 | 33.210 | -4.806 |
| T8 | 2.65% | 28.482 | 30.106 | -2.372 | 1.98% | 28.482 | 29.911 | -2.357 | 4.96% | 28.482 | 30.785 | -2.426 |
| T9 | 2.65% | 31.547 | 29.236 | -0.870 | 1.98% | 31.547 | 29.047 | -0.864 | 4.96% | 31.547 | 29.896 | -0.889 |
| T10 | 2.65% | 28.240 | 32.382 | 3.146 | 1.98% | 28.240 | 32.173 | 3.126 | 4.96% | 28.240 | 33.112 | 3.217 |
| T11 | 2.65% | 33.439 | 28.988 | -3.394 | 1.98% | 33.439 | 28.801 | -3.372 | 4.96% | 33.439 | 29.642 | -3.470 |
| T12 | 2.65% | 30.182 | 34.324 | 5.336 | 1.98% | 30.182 | 34.102 | 5.302 | 4.96% | 30.182 | 35.098 | 5.456 |
| T13 | 2.65% | 23.434 | 30.981 | -3.343 | 1.98% | 23.434 | 30.781 | -3.321 | 4.96% | 23.434 | 31.680 | -3.418 |
| T14 | 2.65% | 23.730 | 24.054 | -6.927 | 1.98% | 23.730 | 23.898 | -6.882 | 4.96% | 23.730 | 24.596 | -7.083 |
| T15 | 2.65% | 22.131 | 24.358 | 0.304 | 1.98% | 22.131 | 24.200 | 0.302 | 4.96% | 22.131 | 24.907 | 0.311 |
| T16 | 2.65% | 24.278 | 22.717 | -1.641 | 1.98% | 24.278 | 22.570 | -1.630 | 4.96% | 24.278 | 23.229 | -1.678 |
El rendimiento esperado trimestral se calcula por CAPM como \(E[R_{trim}] = R_{f,trim} + \beta \times (E[R_{m,trim}] - R_{f,trim})\) (Rodríguez, 2026b). El valor esperado de cada trimestre corresponde al valor de la cartera del trimestre anterior multiplicado por \((1 + E[R_{trim}])\). El resultado trimestral es la diferencia entre el valor esperado del trimestre actual y el del trimestre anterior, representando la ganancia o pérdida neta de la cobertura en ese período.
Con beta real de 0.834, el portafolio tiene una exposición sistemática moderada y requiere 60 contratos. Con beta = 0.5 la sensibilidad al mercado se reduce a la mitad, generando menores rendimientos esperados en períodos alcistas pero mayor protección en períodos bajistas. Con beta = 2.0 el portafolio amplifica los movimientos del índice, lo que en períodos alcistas genera mayor valor esperado pero en períodos bajistas profundiza las pérdidas, haciendo la cobertura más crítica y costosa.
div_tabla <- data.frame(
Empresa = c("Adobe Inc. (ADBE)",
"Vertex Pharmaceuticals (VRTX)",
"Monster Beverage Corp. (MNST)"),
Div_yield = c("0 %", "0 %", "0 %"),
Politica = c("No paga dividendos. Reinvierte mediante recompra de acciones (StockAnalysis, 2026a)",
"No paga dividendos. Reinvierte en I+D y adquisiciones estratégicas (Yahoo Finance, 2026b)",
"No paga dividendos. Reinvierte en crecimiento orgánico e internacional (Yahoo Finance, 2026c)"),
Impacto_cob = c("Sin ajuste al precio futuro por dividendos. q = 0 en F₀ = S₀·e^(r-q)T",
"Sin ajuste al precio futuro por dividendos. q = 0 en F₀ = S₀·e^(r-q)T",
"Sin ajuste al precio futuro por dividendos. q = 0 en F₀ = S₀·e^(r-q)T")
)
kable(div_tabla,
col.names = c("Empresa","Dividend Yield",
"Política de dividendos (fuente verificable)",
"Impacto en la cobertura con futuros"),
caption = "Tabla. Política de dividendos y efecto sobre el precio teórico del futuro") |>
kable_styling(bootstrap_options = c("striped","hover"), full_width = TRUE) |>
column_spec(1, bold = TRUE)| Empresa | Dividend Yield | Política de dividendos (fuente verificable) | Impacto en la cobertura con futuros |
|---|---|---|---|
| Adobe Inc. (ADBE) | 0 % | No paga dividendos. Reinvierte mediante recompra de acciones (StockAnalysis, 2026a) | Sin ajuste al precio futuro por dividendos. q = 0 en F₀ = S₀·e^(r-q)T |
| Vertex Pharmaceuticals (VRTX) | 0 % | No paga dividendos. Reinvierte en I+D y adquisiciones estratégicas (Yahoo Finance, 2026b) | Sin ajuste al precio futuro por dividendos. q = 0 en F₀ = S₀·e^(r-q)T |
| Monster Beverage Corp. (MNST) | 0 % | No paga dividendos. Reinvierte en crecimiento orgánico e internacional (Yahoo Finance, 2026c) | Sin ajuste al precio futuro por dividendos. q = 0 en F₀ = S₀·e^(r-q)T |
Las tres acciones del portafolio no pagan dividendos en efectivo, lo cual fue verificado en las fuentes indicadas. Esto simplifica la valoración de los contratos de futuros: el precio teórico del futuro se calcula como \(F_0 = S_0 \cdot e^{r \cdot T}\), donde el término de dividend yield \(q = 0\). Si alguna empresa pagara dividendos, el precio spot caería el día ex-dividendo y el precio teórico del futuro sería \(F_0 = S_0 \cdot e^{(r-q) \cdot T}\), generando un ajuste a la baja en el precio forward y, por tanto, en el número de contratos necesarios (Hull, 2009).
Adobe Inc. (2025). Adobe reports record Q4 and FY2025 results [Form 8-K]. U.S. Securities and Exchange Commission. https://www.sec.gov/Archives/edgar/data/0000796343/000079634325000135/adbeex991q425.htm
Benzinga. (2026a). Adobe (ADBE) stock price prediction 2026. Recuperado el 30 de abril de 2026, de https://www.benzinga.com/money/adobe-stock-price-prediction
Benzinga. (2026b). Vertex Pharmaceuticals (VRTX) analyst ratings and price targets. Recuperado el 30 de abril de 2026, de https://www.benzinga.com/quote/VRTX/analyst-ratings
Bodie, Z., Kane, A., & Marcus, A. (2008). Investments (8.ª ed.). McGraw-Hill.
DividendMax. (2026). Dividend history and forecasts – ADBE, VRTX, MNST. Recuperado el 30 de abril de 2026, de https://www.dividendmax.com
Elton, E. J., Gruber, M. J., Brown, S. J., & Goetzmann, W. N. (2014). Modern portfolio theory and investment analysis (9.ª ed.). John Wiley & Sons.
Fabozzi, F. J., Kolm, P. N., Pachamanova, D., & Focardi, S. M. (2007). Robust portfolio optimization and management. John Wiley & Sons.
CME Group. (2026). E-mini S&P 500 futures contract specifications. https://www.cmegroup.com/markets/equities/sp/e-mini-sandp500.contractSpecs.html
Federal Reserve/CBOE. (2026). ^TNX – CBOE Interest Rate 3-5 Year Treasury. https://finance.yahoo.com/quote/%5ETNX/
FINVIZ. (2026). Stock screener – Fundamentals. https://finviz.com/screener.ashx
FullRatio. (2026). ADBE 2026 earnings & EPS history. https://fullratio.com/stocks/nasdaq-adbe/earnings
Hull, J. C. (2009). Introducción a los mercados de futuros y opciones (6.ª ed.). Pearson Education.
Markowitz, H. (1952). Portfolio selection. The Journal of Finance, 7(1), 77–91. https://doi.org/10.2307/2975974
Investing.com. (2026). Vertex (VRTX) stock forecast & price target. https://www.investing.com/equities/vertex-pharm-consensus-estimates
Monster Beverage Corporation. (2026). Monster Beverage reports 2026 first quarter financial results [Form 8-K]. U.S. Securities and Exchange Commission. https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0000865752&type=8-K
Rodríguez, D. E. (2026a). Derivados financieros – Forwards. Instituto Tecnológico Metropolitano.
Rodríguez, D. E. (2026b). Derivados financieros – Futuros. Instituto Tecnológico Metropolitano.
Rodríguez, D. E. (2026c). Clase derivados financieros 08/05/2026 [Grabación de reunión virtual]. Instituto Tecnológico Metropolitano. https://correoitmedu-my.sharepoint.com
Serrano, J. (2005). Mercados financieros. Universidad de los Andes.
StockAnalysis. (2026a). Adobe (ADBE) stock price & overview. https://stockanalysis.com/stocks/adbe/
StockAnalysis. (2026b). Vertex Pharmaceuticals (VRTX) stock price. https://stockanalysis.com/stocks/vrtx/forecast/
StockAnalysis. (2026c). Monster Beverage (MNST) stock price. https://stockanalysis.com/stocks/mnst/
TickerNerd. (2026a). ADBE stock forecast 2026. https://tickernerd.com/stock/adbe-forecast/
TickerNerd. (2026b). VRTX stock forecast 2026. https://tickernerd.com/stock/vrtx-forecast/
TIKR. (2026a). Monster Beverage stock analysis. https://www.tikr.com/blog/monster-beverage-stock-is-up-25-over-the-past-year-heres-what-q1-sales-jump-means-for-2026
TradingView. (2026). Monster Beverage Corporation (MNST) chart. https://www.tradingview.com/symbols/NASDAQ-MNST/
Vertex Pharmaceuticals. (2026a). Vertex reports fourth quarter and full year 2025 financial results. https://investors.vrtx.com
Vertex Pharmaceuticals. (2026b). Vertex reports first quarter 2026 financial results. https://news.vrtx.com/news-releases/news-release-details/vertex-reports-first-quarter-2026-financial-results
Yahoo Finance. (2026a). Adobe Inc. (ADBE). https://finance.yahoo.com/quote/ADBE/
Yahoo Finance. (2026b). Vertex Pharmaceuticals Incorporated (VRTX) – stock price. Recuperado el 30 de abril de 2026, de https://finance.yahoo.com/quote/VRTX/
Yahoo Finance. (2026c). Monster Beverage Corporation (MNST) – stock price, news & history. Recuperado el 30 de abril de 2026, de https://finance.yahoo.com/quote/MNST/