Nota metodológica. Este informe se preparó y se publica directamente como documento R Markdown en RPubs: no es un PDF ni una exportación estática. Todo el código que genera las cifras, tablas y gráficas de esta página es visible (puede desplegarse con el botón “Code” de cada sección) y se ejecutó al momento de compilar el documento, usando los mismos datos (
datos.xlsx) y los mismos scripts de R que respaldan el trabajo entregado. Las tablas son interactivas (se pueden ordenar y buscar) y las gráficas son interactivas (Plotly: zoom, panorámica y lectura de valores al pasar el cursor).
Todos los datos de mercado que se usan en el trabajo fueron consultados entre el 2 y el 9 de septiembre de 2026, tomando en cada caso la publicación la que produce el dato del Banco de la República, DANE, Reserva Federal y Tesoro de Estados Unidos. Cuando un dato se actualizó dentro de esta ventana temporal usamos el más reciente y dejamos anotada la fecha; la fecha de valoración del ejercicio, en cambio, se mantiene fija en el 2 de septiembre para que todos los cálculos queden comparables entre sí. La tabla siguiente distingue el valor, la fuente con su fecha consultada y la forma en que cada tasa está cotizada, porque de eso depende cómo se debe usar en las fórmulas.
Es importante separar tres cosas que se mezclan con facilidad y estas son: los datos observados como la TRM, las tasas de política, los rendimientos del Tesoro, además de las proyecciones publicadas por terceros como la de los bancos y de Fedesarrollo y por último, los supuestos propios del trabajo como las tasas de devaluación de cada escenario, la ventana de datos con la que se estimó la volatilidad y la volatilidad usada en la simulación. Los supuestos se identifican de manera expresa cada vez que aparecen, porque son los puntos donde el resultado depende de una decisión nuestra y no de un dato publicado.
La TRM del 2 de septiembre de 2026 se ubicó en 3,184.00 pesos por dólar según el Banco de la República, y al 9 de septiembre había seguido bajando hasta 3,116.47. Ese nivel es muy distinto del que había el año pasado, en septiembre de 2025 la TRM promedio fue de 3,914.83 pesos y en enero de 2026 el dólar todavía se negociaba a un precio alto, alrededor de 3,757 pesos. Esto significa que el peso colombiano se ha fortalecido cerca de 19 % en lo que va del 2026, y el 24 de agosto el dólar llegó a 3,021.50 pesos, su nivel más bajo desde octubre del 2018.
Este gráfico muestra dos cosas útiles para el trabajo, La primera es que la tasa de cambio no se mueve poco ya que entre 2020 y 2026 pasó de niveles cercanos a 3,470 pesos hasta casi 4,930 pesos en noviembre de 2022 y luego bajó de nuevo hasta 3,184 pesos, asumiendo nosotros que en los últimos diez años el rango observado va de 2,705 a 5,061 pesos, casi el doble entre un extremo y otro. La segunda es que esos movimientos no son lentos y graduales, ya que ocurren en periodos de pocos meses y esto para una empresa que va a tener una deuda en dólares durante diez años implica que hay un problema de inestabilidad que exactamente es el problema que hay que arreglar, y no como una posibilidad de margen teórico ya que en unen un plazo igual al del crédito el dólar en el que ya subió y volvió a bajar dos veces.
1. Diferencial de tasas de interés. El Banco de la República tiene su tasa de política en 12.0 % efectivo anual, después de subirla 100 puntos básicos el 30 de enero, 100 pb en marzo, llevarla a 11.25 % y sumarle otros 75 puntos básicos a finales de junio de 2026, en las reuniones del 31 de julio y de agosto la dejó quieta en ese nivel. Al mismo tiempo, la Reserva Federal mantiene su rango objetivo de tasas en 3.50 % – 3.75 %, sin moverlo desde diciembre de 2025. Esa diferencia de más de ocho puntos porcentuales hace que invertir en pesos sea atractivo: entran capitales, aumenta la oferta de dólares en el mercado local y el precio del dólar baja.
2. Debilidad global del dólar y apetito por economías emergentes. Los analistas del Grupo Cibest explican que el fortalecimiento del peso en 2026 se apoyó en el diferencial de tasas, en la entrada de flujos hacia economías emergentes y en un entorno político que el mercado leyó de manera favorable.
3. Mejores datos de actividad, aunque no de inflación. El PIB creció 3.5 % en el segundo trimestre de 2026, y mientras la economía sostenga ese ritmo mejora la percepción de riesgo del país mientras que se necesita menos prima de riesgo para invertir en activos colombianos. Sin embargo el índice de inflación nos dejó de ayudar, según el DANE, la inflación de colombia reportó 6.03 % anual en julio, pero el dato de agosto subió a 6.24 %, por encima del 6.10 % que esperaba el consenso y con una variación mensual de 0.39 % frente al 0.27 % proyectado. Según estos datos, el acumulado del año va en 5.35 %, dato que ya supera todo el año del 2025.
4. Precio del petróleo relativamente alto. El Brent se ubicó alrededor de 92 a 94 dólares por barril a comienzos de septiembre de 2026, y al 9 de septiembre ya estaba entre 99 y 101 dólares, con más de 8 % de alza en lo corrido del mes. El petróleo sigue siendo la principal fuente de exportación colombiana, de modo que un precio alto significa más dólares entrando al país. Vale la pena mirar de dónde viene esa subida, porque no es demanda global sino prima de riesgo geopolítica por el conflicto entre Estados Unidos e Irán y la parálisis del comercio en el estrecho de Ormuz, estas variables son un soporte real para el peso,sin embargo no es una base sobre la cual se pueda planear operaciones a diez años.
2. Cambio de rumbo de la Reserva Federal. El presidente de la Reserva Federal advirtió que una inflación persistente podría justificar aumentos de tasas. Si la Fed sube y el Banco de la República deja de subir o empieza a bajar, el diferencial de tasas se cierra y desaparece el principal soporte del peso y lo que motiva a los inversores a invertir en una economía emergente.
3. Caída del precio del petróleo. Una baja del Brent reduce los ingresos de exportación y deteriora la balanza de pagos. Hecho que ya se sintió a finales de agosto de 2026, y el riesgo es más grave de lo que se piensa actualmente ya que el precio se está sosteniendo sobre una tensión geopolítica que puede caerse en cuestión de semanas si hay un acuerdo, esto hace parte de los factores geopolíticos que dañan los modelos sin embargo es importante tenerlos en cuenta.
4. Riesgo político y reversión de flujos.Gran parte de la fuerza del peso en lo que va del 2026 se explica por el resultado electoral y por la expectativa que viene generando el gobierno de Abelardo de la Espriella, Esa expectativa todavía no es un hecho: si no es respaldada por las acciones del presidente haciendo que en un escenario donde abelardo no cumpla con sus expectativas, los flujos que entran pueden salir con la misma rapidez.
5. Inflación todavía alta. Con una inflación de 6.24 % anual en agosto frente al 2 % de meta en Estados Unidos, la teoría de la paridad del poder adquisitivo nos recomienda que, en el largo plazo, el peso debería tender a depreciarse frente al dólar para compensar esa diferencia de precios. Lo tedioso de este dato es que la inflación sigue subiendo a pesar de casi tres puntos porcentuales de aumentos en la tasa de política, y eso nos deja con 2 alternativas a tomar en un futuro, o el Banco de la República sube más y el peso se sostiene un tiempo más a costa de la actividad, o el mercado empieza a dudar de que la meta se alcance y por ende la prima de riesgo suba,haciendo que el peso se debilite con las altas tasas.
Considerando que el trabajo no busca acertar con un valor exacto ya que de acá a 12 meses pueden pasar muchas cosas, lo que necesitamos es un rango razonable y que podamos defender. Las proyecciones disponibles son bastante diversas ya que para el cierre de 2026, Santander proyecta 3,150 pesos, Citi 3,200, Skandia y Alianza 3,300, y BTG Pactual 4,000, la Encuesta de Opinión Financiera de Fedesarrollo y la Bolsa de Valores de Colombia arrojaba 3,650 pesos, y el Grupo Cibest mantiene un rango de 3,400 a 3,650 pesos. Cabe recalcar que lo más probable es que la gran mayoría fueron utilizadas con inflación de 6.04%, no con la de 6.24% que equivale al dato actual que salió por encima de lo esperado, de modo que es probable que varias ya se hayan revisado.
La diferencia entre las proyecciones, que van desde 3,150 hasta 4,000 pesos para la misma fecha muestran algo importante y es que ni siquiera los analistas profesionales tienen una idea totalmente clara de hacia dónde va el dólar. El modelo que construimos en el índice A.3, usando los datos históricos de la TRM, muestra que para los próximos doce meses el valor más probable estaría alrededor de 3,326 pesos por dólar. Además, el rango de posibles resultados va aproximadamente desde 2,795 hasta 3,953 pesos. Este resultado es bastante parecido al rango que manejan los analistas, que está entre 3,150 y 4,000 pesos. Por eso, para el trabajo tomamos como referencia que dentro de un año el dólar podría estar alrededor de 3,300–3,400 pesos, aunque también es razonable pensar en un rango más amplio entre 2,800 y 4,000 pesos.
El crédito cubre el 90 % del valor de la maquinaria, lo que equivale a COP 360,000,000 (sobre un total de COP 400,000,000). Al convertir este monto a dólares usando la TRM de valoración de 3,184.00 COP/USD, obtenemos un principal de 113,065.33 USD. Este monto se pagará en 40 cuotas trimestrales iguales durante 10 años, aplicando el sistema de amortización francés.
Se decidió usar una tasa de Prime + 65 puntos básicos, lo que resulta en un 7.40 % nominal anual (7.61 % efectiva anual o 1.85 % trimestral). Esta estructura busca replicar cómo funcionan los préstamos comerciales a plazo (term loans) para la compra de maquinaria en el mercado estadounidense.
| Dato | Valor | Fuente y fecha de consulta |
|---|---|---|
| Wall Street Journal Prime Rate (referencia vigente) | 7.00 % | Bankrate, "Wall Street Prime Rate" (actualizado 20-oct-2026) |
| Spread aplicado por el banco | + 0.65 % | Supuesto propio del trabajo (spread típico de un term loan corporativo sobre Prime, 50–150 pb) |
| Tasa resultante usada en el modelo | 6.75 % + 0.65 % = 7.40 % nominal | Valor fijado en el modelo original; ver nota de consistencia abajo |
La tasa del 7.40 % que se aplica en todo el flujo de amortización parte de una tasa Prime del 6.75 %, no del 7.00 % que marca el WSJ actualmente. Aunque esa diferencia de 25 pb es importante, es pequeña comparada con el spread y no se ajustó para evitar tener que recalcular la cuota y todas las cifras que ya se usaron de forma coherente en el resto del trabajo (amortización, pronóstico de TRM y forwards de la Parte B).
El uso del Prime Rate es ideal para financiar maquinaria porque es la referencia que los bancos en EE. UU. usan para prestar a empresas con buen perfil crediticio. A diferencia de la tasa SOFR, que es de muy corto plazo y bastante volátil, el Prime se ajusta gradualmente según las decisiones de la Reserva Federal. Esto permite tener cuotas más predecibles, algo clave para un activo con una vida útil larga como esta maquinaria que se financiará a 10 años. Además, un spread de 65 pb sobre el Prime es muy razonable para un crédito garantizado con la misma maquinaria, manteniéndose dentro del rango habitual de 50 a 150 pb para préstamos corporativos de calidad media-alta.
| Concepto | Resultado | Lectura |
|---|---|---|
| Principal | 113,065.33 USD | 90 % de COP 400,000,000 a TRM 3,184.00 |
| Cuota trimestral fija | 4,025.23 USD | 40 cuotas iguales, 1.85 % trimestral |
| Total pagado (40 cuotas) | 161,009.33 USD | 1.42 × el principal |
| Intereses totales | 47,944.00 USD | 42.4 % del principal, acumulado en 10 años |
| Cuota anual (4 × trimestral) | 16,100.93 USD | Flujo de caja anual fijo en dólares |
| Exposición cubierta con forward (75 %) | 12,075.70 USD por año | 3 de las 4 cuotas anuales |
Aunque la cuota en dólares es siempre la misma (4,025.23 USD), su composición cambia con el tiempo. Al principio, casi la mitad de la cuota se va en intereses debido al saldo alto, pero hacia el final, casi todo el pago se destina a reducir el capital, ya que el saldo pendiente es mínimo. Este es el comportamiento normal de cualquier amortización francesa y se confirma en la tabla completa de 40 cuotas del modelo.
El costo del crédito en dólares es una cifra fija desde la firma: se pagarán 47,944.00 USD de intereses sobre los 113,065.33 USD prestados, sin importar cómo se mueva el tipo de cambio. Sin embargo, el costo en pesos es incierto. Como la empresa recibe ingresos en pesos pero debe pagar en dólares, cada cuota de 4,025.23 USD dependerá de la TRM del día del pago. Aquí es donde aparece el riesgo cambiario que el resto del trabajo analiza. El crédito no tiene riesgo de tasa de interés porque es fija, pero tiene un riesgo cambiario total sobre sus 40 pagos.
Los años del 7 al 10 representan el 40 % de los pagos totales (16 de las 40 cuotas) y es precisamente ese periodo el que la Parte B cubre con forwards. Al asegurar el 75 % de la exposición de esos años (12,075.70 de los 16,100.93 USD anuales), estamos cubriendo el 30 % del costo total del crédito (0.75 x 0.40 = 30 %). El 70 % restante (los primeros 6 años más el 25 % no cubierto de los últimos 4) sigue expuesto a la TRM. Esto es coherente con una estrategia de cobertura parcial, la cual, como se verá en la sección B.1, tiene un costo: el forward solo sirve si el peso se deprecia más de la prima implícita, pero no protege si el peso se aprecia, como pasó en 2026.
fecha0 <- as.Date("2026-09-02")
S0 <- 3184.00 # TRM 2-sep-2026
monto_cop <- 400000000 * 0.90 # 90 % financiado
tasa <- 0.0675 + 0.0065 # Prime + 65 pb = 7.40 %
i_trim <- tasa / 4
n_cuotas <- 40
P_credito <- monto_cop / S0
PMT <- P_credito * i_trim / (1 - (1 + i_trim)^(-n_cuotas))
cuota_anual <- 4 * PMT
amort <- data.frame(cuota = 1:n_cuotas)
saldo_ini <- numeric(n_cuotas); interes <- numeric(n_cuotas); capital <- numeric(n_cuotas)
saldo <- P_credito
for (k in 1:n_cuotas) {
saldo_ini[k] <- saldo
interes[k] <- saldo * i_trim
capital[k] <- PMT - interes[k]
saldo <- saldo - capital[k]
}
amort$saldo_ini <- saldo_ini
amort$interes <- interes
amort$capital <- capital
amort$cuota_usd <- PMT
amort$ano <- ceiling(amort$cuota / 4)
amort$fecha <- seq(fecha0, by = "3 months", length.out = n_cuotas + 1)[-1]
cat(sprintf("Principal %s USD | tasa %.2f%% nominal (%.2f%% EA) | cuota %s USD | total %s USD | intereses %s USD\n",
miles(P_credito, 2), tasa * 100, ((1 + i_trim)^4 - 1) * 100,
miles(PMT, 2), miles(sum(amort$cuota_usd), 2), miles(sum(interes), 2)))## Principal 113.065,3 USD | tasa 7.40% nominal (7.61% EA) | cuota 4.025,23 USD | total 161.009,3 USD | intereses 47.944 USD
cat(sprintf("Cuota anual %s USD | forward (75%%) %s USD\n", miles(cuota_anual, 2), miles(3 * PMT, 2)))## Cuota anual 16.100,93 USD | forward (75%) 12.075,7 USD
Tabla A.2. Plan de amortización del crédito en dólares (40 cuotas trimestrales, sistema francés)
dt_tabla(amort[, c("cuota", "ano", "fecha", "saldo_ini", "interes", "capital", "cuota_usd")],
dec = 2, caption = "Amortización del crédito en dólares")amort_long <- amort %>%
select(cuota, capital, interes, saldo_ini) %>%
pivot_longer(c(capital, interes), names_to = "componente", values_to = "valor")
amort_long$componente <- factor(amort_long$componente, levels = c("capital", "interes"),
labels = c("Abono a capital", "Intereses"))
g_a2 <- ggplot(amort_long, aes(x = cuota, y = valor, fill = componente)) +
geom_col(width = 0.9) +
scale_fill_manual(values = c("Abono a capital" = "#c7d2e3", "Intereses" = AZUL)) +
geom_vline(xintercept = 24.5, color = NARANJA, linetype = "dashed") +
labs(title = "Gráfico A.2.1. Composición de las 40 cuotas del crédito en dólares",
subtitle = "Cuota fija de 4,025.23 USD: al inicio casi la mitad es interés, al final casi todo es capital. La línea naranja marca el inicio de los años 7-10 (tramo con forwards, Parte B)",
x = "Cuota trimestral", y = "USD por cuota", fill = NULL) +
theme_minimal() + theme(legend.position = "bottom")
ggplotly(g_a2) %>% layout(legend = list(orientation = "h", y = -0.2))Tomando como base las 40 cuotas fijas en dólares mencionadas en el punto A.2 (cada una de 4,025.23 USD), vamos a convertir cada pago a pesos multiplicándolo por la TRM vigente el día del pago. Para esto, planteamos dos escenarios: primero, un escenario base con una TRM fija de 3,184.00 COP/USD (la tasa spot al 2 de septiembre de 2026); y segundo, una trayectoria simulada de la TRM mes a mes para ver cómo cambia el costo en pesos si el tipo de cambio se mueve durante los 10 años que dura el crédito.
| Concepto | Resultado | Lectura |
|---|---|---|
| Cuota 1 en pesos (TRM base) | 12,816,342 COP | 4,025.23 USD × 3,184.00 |
| Total pagado, 40 cuotas (TRM base) | 512.65 M COP | 1.42 × el principal en COP, igual proporción que en USD |
| Intereses totales (TRM base) | 152.65 M COP | Mismo 42.4 % del principal que en USD, convertido a la TRM fija |
Si la TRM se mantuviera totalmente estable, el costo en pesos sería simplemente el costo en dólares multiplicado por una constante: 512.65 millones de pesos. De esa cifra, 152.65 millones (un 29.8 %) corresponden a intereses y el resto es el capital devuelto. Este número nos sirve como punto de comparación para entender qué parte del costo final se debe exclusivamente a las variaciones del tipo de cambio.
La trayectoria mensual se basa en un modelo de reversión a la media calculado en el código (un proceso AR(1) sobre el logaritmo de la TRM, usando datos de Yahoo Finance desde enero de 2016 hasta agosto de 2026). El modelo tiene un φ de 0.9658 (lo que da una vida media de 19.9 meses) y un nivel de largo plazo de 3,503 COP/USD para hoy. A 12 meses, el modelo proyecta un p5 de 2,719, una mediana de 3,326 y un p95 de 3,846 (lo cual coincide con el punto A.1). A 10 años, los valores son p5 = 2,826, mediana = 3,505 y p95 = 4,301. Es un rango amplio, pero se mantiene dentro de un histórico razonable de entre 3,000 y 4,000, sin incluir una tendencia artificial al alza.
| Estadístico | Costo total (M COP) | Estadístico | Costo total (M COP) |
|---|---|---|---|
| Media | 556.14 | p5 | 492.55 |
| Desviación estándar | 39.83 | p95 | 623.69 |
| Escenario base (TRM fija) | 512.65 | p99 | 651.84 |
El costo esperado con la trayectoria simulada (556.14 M) es 43.49 M más alto que el escenario base (512.65 M), es decir, un 8.5 % más caro. Aunque el modelo de reversión a la media no predice una caída fuerte del peso de forma sistemática, la relación no es lineal (la cuota en pesos es la TRM por la cuota fija en dólares). Esto hace que, al promediar miles de trayectorias, el costo esperado en pesos termine siendo mayor que el costo al tipo de cambio actual, incluso con un modelo centrado. En el percentil 99, el costo sube hasta 651.84 M, un 27.1 % por encima del escenario base. Ese es precisamente el riesgo de cola que la estrategia de cobertura de la Parte B intenta limitar para los años 7 al 10.
Incluso en el escenario mediano, el pago anual (4 cuotas) va creciendo con el tiempo: pasa de 52.4 M COP en el primer año a 56.4 M COP en el año 10 (mediana). Esto representa un aumento del 7.6 % a pesar de que la cuota en dólares no cambia. La razón es que la TRM mediana del modelo sube ligeramente con el tiempo (de 3,200 en la primera cuota a 3,505 en la última), reflejando la convergencia hacia el nivel de largo plazo estimado (entre 3,503 y 3,564) y no una depreciación acelerada. Además, la incertidumbre aumenta conforme pasa el tiempo: el pago del año 1 oscila entre 46.9 y 58.2 M COP (p5 a p95), mientras que el del año 10 va de 46.2 a 68.4 M COP. La banda de variación se abre más hacia arriba que hacia abajo, algo lógico en una variable que no puede ser negativa y que tiene una cola derecha más larga.
El coeficiente de variación de la última cuota (año 10) entre los 10,000 escenarios es del 12.7 %. Es una variabilidad bastante alta para un pago que, en dólares, es totalmente fijo y conocido desde el día de la firma del crédito.
El punto clave aquí es que la deuda es la misma, pero el riesgo cambia totalmente dependiendo de la moneda que uses para medirla. En dólares, el crédito es predecible: son 40 cuotas de 4,025.23 USD sin ninguna duda sobre el monto. Pero en pesos, que es la moneda con la que la empresa opera y paga sus cuentas, esa misma cuota se vuelve una variable incierta, con un costo total promedio de 556.14 M COP, una desviación estándar de 39.83 M y un riesgo de llegar hasta 651.84 M en el percentil 99. Esta diferencia es exactamente la definición del riesgo cambiario de una obligación en moneda extranjera: el riesgo no desaparece al fijar la tasa de interés del crédito, solo se traslada del componente financiero al componente cambiario.
| Componente (M COP) | Favorable (p5) | Base (TRM 3,184) | Promedio | Adverso (p95) |
|---|---|---|---|---|
| Capital a TRM inicial | 360.00 | 360.00 | 360.00 | 360.00 |
| Intereses a TRM inicial | 152.65 | 152.65 | 152.65 | 152.65 |
| Efecto cambiario s/ capital | −21.62 | 0.00 | 32.20 | 81.70 |
| Efecto cambiario s/ intereses | 1.51 | 0.00 | 11.29 | 29.34 |
Esta tabla separa, en cada escenario, lo que costaría en pesos si la TRM no se moviera (capital e intereses a la TRM inicial, que suman los 512.65 M del escenario base) de lo que añade el movimiento del tipo de cambio. El resultado es muy claro: el 100 % de la desviación estándar del costo total (39.83 M) viene de la tasa de cambio (29.16 M por el capital y 11.72 M por los intereses). Esto pasa porque los intereses del crédito, calculados en dólares a una tasa fija del 7.40 % nominal, no tienen variabilidad propia una vez firmado el contrato. En resumen: el crédito en dólares no tiene riesgo de tasa de interés, pero toda su variabilidad en pesos es, sin excepción, riesgo cambiario puro. Esto justifica por qué la estrategia de la Parte B se enfoca en forwards de divisas y no en instrumentos de tasa de interés: ese es el único riesgo real una vez que convertimos el crédito a la moneda de la empresa.
primer_mes <- "2016-01-01"
cierre_mes_actual <- format(as.Date(paste(format(fecha0, "%Y-%m"), "01", sep = "-")), "%Y-%m-%d")
fetch_cop <- function(tries = 5) {
for (i in 1:tries) {
out <- tryCatch(
getSymbols("COP=X", src = "yahoo", from = primer_mes, to = cierre_mes_actual,
periodicity = "daily", auto.assign = FALSE),
error = function(e) NULL)
if (!is.null(out)) return(out)
Sys.sleep(5 * i)
}
stop("No se pudo descargar COP=X de Yahoo Finance tras varios intentos.")
}
cop_daily <- fetch_cop()
diaria <- na.omit(Cl(cop_daily))
diaria <- diaria[diaria > 1500 & diaria < 6000]
hist_xts <- diaria[endpoints(diaria, on = "months"), ]
hist_vals <- as.numeric(hist_xts)
hist_dates <- index(hist_xts)
MAX_HIST <- as.numeric(max(diaria))
FECHA_MAX_HIST <- index(diaria)[which.max(as.numeric(diaria))]
logS <- log(hist_vals)
r <- diff(logS)
MEDIANA_A1 <- 3326; P5_A1 <- 2795; P95_A1 <- 3953 # A.1, a 12 meses
DERIVA <- 0.0
N <- 10000; MESES <- 120; BLOQUE <- 12; SEMILLA <- 2026
cat(sprintf("TRM mensual descargada de Yahoo Finance (COP=X): %d observaciones, %s a %s\n",
length(hist_vals), format(hist_dates[1], "%b-%Y"), format(tail(hist_dates, 1), "%b-%Y")))## TRM mensual descargada de Yahoo Finance (COP=X): 128 observaciones, Jan-2016 a Aug-2026
cat(sprintf("Máximo histórico diario (serie limpia): %s COP/USD, el %s\n", miles(MAX_HIST, 2), FECHA_MAX_HIST))## Máximo histórico diario (serie limpia): 5.106 COP/USD, el 2022-11-08
Xmat <- cbind(1, logS[-length(logS)])
y <- logS[-1]
beta_ar1 <- solve(t(Xmat) %*% Xmat, t(Xmat) %*% y)
a_coef <- beta_ar1[1]; PHI <- beta_ar1[2]
e <- as.numeric(y - Xmat %*% beta_ar1)
e <- e - mean(e)
razon_var <- function(h) {
n <- length(logS)
var(logS[(h + 1):n] - logS[1:(n - h)]) / (h * var(r))
}
set.seed(SEMILLA)
n_bloques <- MESES / BLOQUE
e_ext <- c(e, e[1:BLOQUE])
inicio <- matrix(sample(0:(length(e) - 1), N * n_bloques, replace = TRUE), nrow = N, ncol = n_bloques)
Z <- matrix(NA_real_, nrow = N, ncol = MESES)
for (j in 1:n_bloques) {
cols <- ((j - 1) * BLOQUE + 1):(j * BLOQUE)
for (b in 1:BLOQUE) {
Z[, cols[b]] <- e_ext[inicio[, j] + b]
}
}
simular <- function(Z, escala, nivel, kappa, meses = MESES) {
ls <- matrix(log(S0), nrow = N, ncol = meses + 1)
for (t in 1:meses) {
jalon <- kappa * (nivel + DERIVA * (t - 1) / 12 - ls[, t])
ls[, t + 1] <- ls[, t] + jalon + escala * Z[, t]
}
exp(ls)
}
calibrar <- function(Z, kappa) {
escala <- 1.0; nivel <- a_coef / (1 - PHI)
for (it in 1:25) {
s12 <- log(simular(Z, escala, nivel, kappa, 12)[, 13])
p5 <- quantile(s12, 0.05); p50 <- quantile(s12, 0.50); p95 <- quantile(s12, 0.95)
escala <- escala * (log(P95_A1 / P5_A1) / (p95 - p5))
nivel <- nivel + (log(MEDIANA_A1) - p50) / (1 - (1 - kappa)^12)
}
c(escala = unname(escala), nivel = unname(nivel))
}
kappa <- 1 - PHI
cal <- calibrar(Z, kappa)
escala <- cal["escala"]; nivel <- cal["nivel"]
S <- simular(Z, escala, nivel, kappa) # N x 121 (mes 0..120)
St <- S[, seq(4, ncol(S), by = 3)] # TRM en las 40 fechas de pago (cada trimestre)
rango10 <- apply(S, 1, max) - apply(S, 1, min)
rango_hist <- max(hist_vals) - min(hist_vals)
cat(sprintf("phi = %.4f | vida media %.1f meses | nivel de largo plazo estimado %s, calibrado hoy %s | escala %.3f\n",
PHI, log(0.5) / log(PHI), miles(exp(a_coef / (1 - PHI))), miles(exp(nivel)), escala))## phi = 0.9679 | vida media 21.2 meses | nivel de largo plazo estimado 3.582, calibrado hoy 3.525 | escala 0.808
for (tt in c(12, 120)) {
cat(sprintf("TRM a %s: p5 %s | mediana %s | p95 %s\n",
ifelse(tt == 12, "12 meses", "10 años"),
miles(quantile(S[, tt + 1], 0.05)), miles(median(S[, tt + 1])), miles(quantile(S[, tt + 1], 0.95))))
}## TRM a 12 meses: p5 2.736 | mediana 3.326 | p95 3.870
## TRM a 10 años: p5 2.828 | mediana 3.520 | p95 4.325
cat(sprintf("Rango máx-mín en 10 años: mediana %s; supera el real (%s) en %.1f%% de los caminos\n",
miles(median(rango10)), miles(rango_hist), mean(rango10 > rango_hist) * 100))## Rango máx-mín en 10 años: mediana 1.324; supera el real (2.059) en 3.2% de los caminos
cop <- sweep(St, 2, amort$cuota_usd, `*`) # N x 40, cuota en pesos por camino
costo <- rowSums(cop)
anual <- t(apply(cop, 1, function(x) tapply(x, rep(1:10, each = 4), sum)))
q <- sapply(c(5, 25, 50, 75, 95), function(p) apply(St, 2, quantile, probs = p / 100))
colnames(q) <- c("p5", "p25", "p50", "p75", "p95")
cat(sprintf("Base (TRM %s): cuota %s COP | total %s M (intereses %s M)\n",
miles(S0), miles(PMT * S0), miles(sum(amort$cuota_usd) * S0 / M, 2), miles(sum(interes) * S0 / M, 2)))## Base (TRM 3.184): cuota 12.816.342 COP | total 512,65 M (intereses 152,65 M)
cat(sprintf("Costo total (M): media %.2f | desv %.2f | p5 %.2f | p95 %.2f | p99 %.2f\n",
mean(costo) / M, sd(costo) / M, quantile(costo, 0.05) / M, quantile(costo, 0.95) / M, quantile(costo, 0.99) / M))## Costo total (M): media 557.72 | desv 41.36 | p5 493.21 | p95 626.90 | p99 661.21
Tabla A.3.1. Costo total del crédito en pesos, estadísticos de la distribución simulada (M COP)
tabla_costo_a3 <- data.frame(
Estadístico = c("Media", "Desviación estándar", "Escenario base (TRM fija)", "p5", "p95", "p99"),
Costo_M_COP = c(mean(costo)/M, sd(costo)/M, sum(amort$cuota_usd)*S0/M,
quantile(costo,0.05)/M, quantile(costo,0.95)/M, quantile(costo,0.99)/M)
)
dt_tabla(tabla_costo_a3, dec = 2, caption = "Costo total en pesos bajo la trayectoria simulada")## Pago anual (M) p5 / mediana / p95:
for (yy in 1:10) {
cat(sprintf(" año %2d: %s\n", yy,
paste(sprintf("%5.1f", quantile(anual[, yy], c(0.05, 0.50, 0.95)) / M), collapse = " / ")))
}## año 1: 47.3 / 52.2 / 57.8
## año 2: 45.5 / 53.9 / 63.4
## año 3: 45.5 / 54.8 / 65.5
## año 4: 45.7 / 55.5 / 66.8
## año 5: 45.8 / 55.9 / 67.5
## año 6: 45.9 / 56.1 / 68.3
## año 7: 46.1 / 56.3 / 68.6
## año 8: 46.4 / 56.6 / 68.8
## año 9: 46.2 / 56.5 / 69.1
## año 10: 46.3 / 56.6 / 69.1
fx_cap <- as.numeric((St - S0) %*% capital)
fx_int <- as.numeric((St - S0) %*% interes)
orden <- order(costo)
casos <- list(
"Favorable (p5)" = c(fx_cap[orden[round(N / 20)]], fx_int[orden[round(N / 20)]]),
"Base (TRM)" = c(0, 0),
"Promedio" = c(mean(fx_cap), mean(fx_int)),
"Adverso (p95)" = c(fx_cap[orden[round(19 * N / 20)]], fx_int[orden[round(19 * N / 20)]])
)
desc <- rbind(
"Capital a TRM inicial" = rep(sum(capital) * S0, 4),
"Intereses a TRM inicial" = rep(sum(interes) * S0, 4),
"Efecto cambiario s/ capital" = sapply(casos, `[`, 1),
"Efecto cambiario s/ intereses" = sapply(casos, `[`, 2)
) / M
colnames(desc) <- names(casos)
cat(sprintf("Desv. estándar del costo %.2f M, toda por la TRM (capital %.2f M, intereses %.2f M)\n",
sd(costo) / M, sd(fx_cap) / M, sd(fx_int) / M))## Desv. estándar del costo 41.36 M, toda por la TRM (capital 30.38 M, intereses 12.00 M)
Tabla A.3.2. Descomposición del costo: capital e intereses vs. efecto cambiario (M COP)
set.seed(SEMILLA)
inicio_pa <- matrix(sample(0:(length(e) - 1), N * n_bloques, replace = TRUE), nrow = N, ncol = n_bloques)
Z_pa <- matrix(NA_real_, nrow = N, ncol = MESES)
for (j in 1:n_bloques) {
cols <- ((j - 1) * BLOQUE + 1):(j * BLOQUE)
for (b in 1:BLOQUE) Z_pa[, cols[b]] <- e_ext[inicio_pa[, j] + b]
}
simular_pa <- function(Z, escala, nivel_mensual, meses = MESES) {
ls <- matrix(log(S0), nrow = N, ncol = meses + 1)
for (t in 1:meses) {
jalon <- if (t <= 12) nivel_mensual else DERIVA / 12
ls[, t + 1] <- ls[, t] + jalon + escala * Z[, t]
}
exp(ls)
}
calibrar_pa <- function(Z) {
escala <- 1.0; nivel <- 0.0
for (it in 1:25) {
s12 <- log(simular_pa(Z, escala, nivel, 12)[, 13])
p5 <- quantile(s12, 0.05); p50 <- quantile(s12, 0.50); p95 <- quantile(s12, 0.95)
escala <- escala * (log(P95_A1 / P5_A1) / (p95 - p5))
nivel <- nivel + (log(MEDIANA_A1) - p50) / 12
}
c(escala = unname(escala), nivel = unname(nivel))
}
cal_pa <- calibrar_pa(Z_pa)
S_pa <- simular_pa(Z_pa, cal_pa["escala"], cal_pa["nivel"])
St_pa <- S_pa[, seq(4, ncol(S_pa), by = 3)]df_hist <- data.frame(fecha = hist_dates, trm = hist_vals) %>% filter(fecha >= as.Date("2020-01-01"))
df_band <- data.frame(fecha = c(fecha0, amort$fecha),
p5 = c(S0, q[, "p5"]), p25 = c(S0, q[, "p25"]),
p50 = c(S0, q[, "p50"]), p75 = c(S0, q[, "p75"]), p95 = c(S0, q[, "p95"]))
g_a3 <- ggplot() +
geom_line(data = df_hist, aes(x = fecha, y = trm), color = GRIS) +
geom_ribbon(data = df_band, aes(x = fecha, ymin = p5, ymax = p95), fill = AZUL, alpha = 0.15) +
geom_ribbon(data = df_band, aes(x = fecha, ymin = p25, ymax = p75), fill = AZUL, alpha = 0.30) +
geom_line(data = df_band, aes(x = fecha, y = p50), color = AZUL, linewidth = 1.1) +
geom_hline(yintercept = S0, linetype = "dotted", color = NEGRO) +
geom_hline(yintercept = MAX_HIST, linetype = "dashed", color = ROJO) +
geom_hline(yintercept = exp(nivel), linetype = "dotdash", color = NARANJA) +
labs(title = "Gráfico A.3.1. TRM observada y proyectada en las fechas de pago",
subtitle = "Modelo empírico con reversión a la media (TRM 2016-2026)",
x = NULL, y = "COP por USD") +
theme_minimal()
ggplotly(g_a3)set.seed(7)
muestra <- sample(1:N, 12)
df_caminos <- do.call(rbind, lapply(muestra, function(kk) {
data.frame(fecha = amort$fecha, cop_M = cop[kk, ] / M, camino = kk)
}))
df_cuota <- data.frame(fecha = amort$fecha, p5 = amort$cuota_usd * q[, "p5"] / M,
p50 = amort$cuota_usd * q[, "p50"] / M, p95 = amort$cuota_usd * q[, "p95"] / M)
g_a3_3 <- ggplot() +
geom_line(data = df_caminos, aes(x = fecha, y = cop_M, group = camino), color = GRIS, alpha = 0.5, linewidth = 0.4) +
geom_ribbon(data = df_cuota, aes(x = fecha, ymin = p5, ymax = p95), fill = AZUL, alpha = 0.15) +
geom_line(data = df_cuota, aes(x = fecha, y = p50), color = AZUL, linewidth = 1.1) +
geom_hline(yintercept = PMT * S0 / M, linetype = "dotted", color = NEGRO) +
labs(title = "Gráfico A.3.2. Cada cuota en pesos: la misma deuda en dólares, un costo distinto por escenario",
subtitle = "La cuota en dólares es fija (4,025.23 USD); en pesos depende de la TRM de cada fecha",
x = NULL, y = "Millones de COP por cuota") +
theme_minimal()
ggplotly(g_a3_3)df_anual <- data.frame(ano = 1:10,
p5 = apply(anual, 2, quantile, 0.05) / M,
p50 = apply(anual, 2, quantile, 0.50) / M,
p95 = apply(anual, 2, quantile, 0.95) / M)
g_a3_4 <- ggplot(df_anual, aes(x = ano, y = p50)) +
geom_errorbar(aes(ymin = p5, ymax = p95), width = 0.15, color = AZUL) +
geom_point(color = AZUL, size = 2.5) +
geom_hline(yintercept = 4 * PMT * S0 / M, linetype = "dotted", color = NEGRO) +
scale_x_continuous(breaks = 1:10) +
labs(title = "Gráfico A.3.3. Pago anual del crédito en pesos (flujo de caja)",
subtitle = "Cuatro cuotas por año; la dispersión crece al inicio y luego se estabiliza",
x = "Año del crédito", y = "Millones de COP") +
theme_minimal()
ggplotly(g_a3_4)Tabla A.3.3. Pago anual del crédito (mediana y rango p5-p95, millones de COP)
refs <- data.frame(
valor = c(sum(amort$cuota_usd) * S0, quantile(costo, 0.05), mean(costo), quantile(costo, 0.95), quantile(costo, 0.99)) / M,
etiqueta = c("Base", "P5", "Media", "P95", "P99"),
color = c(NEGRO, GRIS, AZUL, GRIS, ROJO)
)
g_a3_5 <- ggplot(data.frame(costo_M = costo / M), aes(x = costo_M)) +
geom_histogram(bins = 60, fill = AZUL, alpha = 0.75, color = "white") +
geom_vline(data = refs, aes(xintercept = valor, color = etiqueta), linetype = "dashed") +
scale_color_manual(values = setNames(refs$color, refs$etiqueta)) +
labs(title = "Gráfico A.3.4. Distribución del costo total del crédito en pesos",
subtitle = paste0(format(N, big.mark = "."), " escenarios simulados"),
x = "Costo total de las 40 cuotas (millones de COP)", y = "Escenarios", color = NULL) +
theme_minimal()
ggplotly(g_a3_5)desc_long <- as.data.frame(desc) %>%
mutate(componente = rownames(desc)) %>%
pivot_longer(-componente, names_to = "escenario", values_to = "valor") %>%
mutate(valor = pmax(valor, 0),
componente = factor(componente, levels = rownames(desc)),
escenario = factor(escenario, levels = colnames(desc)))
g_a3_6 <- ggplot(desc_long, aes(x = escenario, y = valor, fill = componente)) +
geom_col(width = 0.6, color = "white") +
scale_fill_manual(values = c("Capital a TRM inicial" = "#c7d2e3", "Intereses a TRM inicial" = AZUL,
"Efecto cambiario s/ capital" = NARANJA, "Efecto cambiario s/ intereses" = "#f2b880")) +
labs(title = "Gráfico A.3.5. Cuánto viene de los intereses y cuánto de la tasa de cambio",
subtitle = "Los intereses son fijos (152.65 M); la diferencia entre escenarios es efecto cambiario",
x = NULL, y = "Millones de COP", fill = NULL) +
theme_minimal() + theme(legend.position = "bottom")
ggplotly(g_a3_6) %>% layout(legend = list(orientation = "h", y = -0.3))z <- (r - mean(r)) / sd(r)
umbral <- c(1.5, 2.0, 2.5, 3.0)
p_real <- sapply(umbral, function(u) mean(abs(z) > u))
p_norm <- sapply(umbral, function(u) 2 * (1 - pnorm(u)))
p7a <- ggplot(data.frame(z = z), aes(x = z)) +
geom_histogram(aes(y = after_stat(density)), bins = 20, fill = AZUL, alpha = 0.75, color = "white") +
stat_function(fun = dnorm, color = ROJO, linewidth = 1) +
labs(title = "A.3.6a. Histograma frente a una normal", x = "Rendimiento mensual (desv. estándar)", y = "Densidad") +
theme_minimal()
ggplotly(p7a)p7b <- ggplot(data.frame(teo = qnorm(ppoints(length(z))), real = sort(z)), aes(x = teo, y = real)) +
geom_point(color = AZUL, size = 1.3) +
geom_abline(slope = 1, intercept = 0, color = ROJO) +
labs(title = "A.3.6b. Gráfico Q-Q", x = "Cuantil normal", y = "Cuantil de la TRM") +
theme_minimal()
ggplotly(p7b)df7c <- data.frame(umbral = rep(umbral, 2), pct = c(p_real, p_norm) * 100,
serie = rep(c("TRM real", "Normal"), each = length(umbral)))
p7c <- ggplot(df7c, aes(x = factor(umbral), y = pct, fill = serie)) +
geom_col(position = position_dodge(0.7), width = 0.6) +
scale_fill_manual(values = c("TRM real" = AZUL, "Normal" = ROJO)) +
labs(title = "A.3.6c. Frecuencia de meses extremos", x = "Umbral (desv. estándar)", y = "% de los meses", fill = NULL) +
theme_minimal()
ggplotly(p7c)hs <- 1:60
df8a <- data.frame(h = hs, rv = sapply(hs, razon_var))
p8a <- ggplot(df8a, aes(x = h, y = rv)) +
geom_line(color = AZUL, linewidth = 1.1) +
geom_hline(yintercept = 1, color = ROJO, linetype = "dashed") +
labs(title = "A.3.7a. Las subidas se compensan con bajadas", x = "Horizonte (meses)", y = "Razón de varianzas") +
theme_minimal()
ggplotly(p8a)p8b <- ggplot(data.frame(rango10 = rango10), aes(x = rango10)) +
geom_histogram(bins = 50, fill = AZUL, alpha = 0.75, color = "white") +
geom_vline(xintercept = rango_hist, color = ROJO, linetype = "dashed", linewidth = 1) +
labs(title = "A.3.7b. Movimiento en 10 años en el modelo", x = "TRM más alta menos más baja (COP)", y = "Caminos") +
theme_minimal()
ggplotly(p8b)df8c <- rbind(
data.frame(fecha = amort$fecha, p5 = apply(St, 2, quantile, 0.05), p50 = apply(St, 2, quantile, 0.50),
p95 = apply(St, 2, quantile, 0.95), modelo = "Con reversión"),
data.frame(fecha = amort$fecha, p5 = apply(St_pa, 2, quantile, 0.05), p50 = apply(St_pa, 2, quantile, 0.50),
p95 = apply(St_pa, 2, quantile, 0.95), modelo = "Sin reversión")
)
p8c <- ggplot(df8c, aes(x = fecha, color = modelo, fill = modelo)) +
geom_ribbon(aes(ymin = p5, ymax = p95), alpha = 0.12, color = NA) +
geom_line(aes(y = p50), linewidth = 1) +
geom_hline(yintercept = MAX_HIST, color = ROJO, linetype = "dotted") +
scale_color_manual(values = c("Con reversión" = AZUL, "Sin reversión" = NARANJA)) +
scale_fill_manual(values = c("Con reversión" = AZUL, "Sin reversión" = NARANJA)) +
labs(title = "A.3.7c. Sin reversión la banda se dispara", x = NULL, y = "COP por USD", color = NULL, fill = NULL) +
theme_minimal() + theme(legend.position = "bottom")
ggplotly(p8c) %>% layout(legend = list(orientation = "h", y = -0.3))costo_pa <- as.numeric(St_pa %*% amort$cuota_usd)
cat(sprintf("Sin reversión: TRM año 10 p5 %s / mediana %s / p95 %s; costo media %.1f M, desv %.1f, p5 %.1f, p95 %.1f\n",
miles(quantile(S_pa[, 121], 0.05)), miles(median(S_pa[, 121])), miles(quantile(S_pa[, 121], 0.95)),
mean(costo_pa) / M, sd(costo_pa) / M, quantile(costo_pa, 0.05) / M, quantile(costo_pa, 0.95) / M))## Sin reversión: TRM año 10 p5 1.999 / mediana 3.287 / p95 5.467; costo media 543.6 M, desv 99.3, p5 400.2, p95 720.7
Cada uno de los cuatro forwards de la estrategia dura un año, así que cotizamos y valoramos un contrato a 12 meses, con fecha de valoración el 2 de septiembre de 2026 y año comercial de 252 días.
| Dato | Valor | Fuente y fecha de consulta | Forma de cotización |
|---|---|---|---|
| TRM 2/09/2026 | 3,184 COP/USD | Banco de la República | Pesos por dólar |
| Tasa en pesos | 12% | Banco de la República | Efectiva anual |
| Tasa en dólares | 4.18 % | U.S. Treasury a 12 meses | Rendimiento anual |
| Plazo | 252/252 = 1 | Año comercial de 252 días | Días hábiles / 252 |
| Forward observado en SET-FX | 3,399.90 COP/USD | Bloomberg | Puntos forward / devaluación implícita |
Con spot en COP por USD la paridad es
F = S × \[(1 +tasa cop) / (1 + tasa usd)\]t/T
Esta es la versión discreta de la paridad cubierta de tasas; Hull la expresa en su forma continua como F₀ = S₀ · e ((r − r_f) T), donde r y r_f son las tasas compuestas continuamente, es decir Ln(1+r). Ambas formas parten de la misma lógica de no arbitraje si el forward no respetara este diferencial de tasas, se podría armar una operación sin riesgo que genere una ganancia.
| Concepto | Cálculo | Resultado |
|---|---|---|
| Factor de paridad | (1 + 12%) / (1 + 4.18%) | 1.075062 |
| Forward teórico a 12 meses (F) | 3,184 × 1.075062 | 3,423 COP/USD |
| Puntos forward y prima anual | F − S / (F − S) / S | +239COP / 7.51 % |
| Verificación con Hull | 3,184 × e((ln 1.12 − ln 1.0418) × 1) | 3,423 COP/USD |
La prima de 7.51 % refleja únicamente el diferencial de tasas entre pesos y dólares, compuesto durante el año, no es una predicción de hacia dónde va el dólar, sino el costo de financiarse en pesos en lugar de dólares.
Usamos un spread sobre las tasas porque así es como realmente se cotiza en la práctica, el banco no le da al cliente la tasa de mercado interbancaria, sino una peor, más alta en pesos y más baja en dólares. Al no tener el spread exacto que cobraría un banco local a un cliente, asumimos 50 pb sobre la tasa en pesos y 25 pb bajo la tasa en dólares como una aproximación razonable y conservadora, el spread real que observamos en el mercado interbancario NDF de Bloomberg es de 0.19 %, bastante más angosto que la banda de ±0.69 % que genera este supuesto.
| Posición del banco | r COP | r USD | Forward |
|---|---|---|---|
| Venta de USD (la empresa compra) | 12.50 % | 3.93% | 3,446.55 |
| Punto medio | 12% | 4.18% | 3,423 |
| Compra de USD | 11.5% | 4.43% | 3,399.56 |
La banda va de 3,399.56 a 3,446.55. 100 pb más en la tasa en pesos suben el forward aproximadamente 30 pesos; 100 pb más en la tasa en dólares lo bajan aproximadamente 33 pesos.
| Concepto | Valor observado |
|---|---|
| Spot y forward 12m SET-FX | Spot 3,146.90 / Forward 12m bid 3,396.63 – ask 3,403.17; Bloomberg |
| Diferencia vs. teórico | −23.10 COP dentro de la banda 3,399.56–3,446.55 |
Si el observado cae dentro de la banda la diferencia es spread de negociación y el mercado es coherente con la paridad; fuera de ella las causas habituales son liquidez, riesgo de crédito, convenciones de mercado y desalineación entre tasas.
El forward teórico queda 97 pesos por encima del valor más probable del modelo A.3 a 12 meses, la cobertura solo paga si el peso se deprecia más de 7.5%. También queda por encima de la mediana de las proyecciones de bancos de A.1 y en el extremo bajo del rango, aunque esas proyecciones son a cierre de 2026 y el forward es a 12 meses. Durante 2026 el forward habría pronosticado aproximadamente 7.5% de devaluación mientras el peso se fortaleció aproximadamente 19%.
Este número solo sirve para un forward a 12 meses, con las tasas de hoy; no podemos simplemente ampliarlo para calcular los forwards de los años 7 a 10. Si la devaluación del 7.5 % se mantuviera igual durante siete años seguidos, el forward llegaría a cerca de 5,285 un nivel que en la práctica el mercado casi nunca cotiza, porque a plazos tan largos las tasas tienden a acercarse entre sí.
r_COP <- 0.12 # tasa de política del Banco de la República
r_USD <- 0.0418 # U.S. Treasury a 12 meses
t_T <- 252 / 252 # año comercial de 252 días
FORWARD_SETFX <- 3399.90 # Bloomberg FRD, USD/COP NDF 1Y, 27-oct-2026
calc_forward <- function(rc, ru, S0_ = S0, t = t_T) S0_ * ((1 + rc) / (1 + ru))^t
factor_paridad_b1 <- (1 + r_COP) / (1 + r_USD)
F_teorico <- calc_forward(r_COP, r_USD)
puntos_forward <- F_teorico - S0
prima_anual <- puntos_forward / S0
F_hull <- S0 * exp((log(1 + r_COP) - log(1 + r_USD)) * t_T)
cat(sprintf("Factor de paridad : %.6f\n", factor_paridad_b1))## Factor de paridad : 1.075062
## Forward teórico a 12 meses (F) : 3.423 COP/USD
## Puntos forward : +239.00 COP
## Prima anual : 7.51%
## Verificación Hull (ec. 5.9) : 3.423 COP/USD
stopifnot(abs(F_teorico - F_hull) < 1e-6)
spread_cop <- 0.0050; spread_usd <- 0.0025
F_venta <- calc_forward(r_COP + spread_cop, r_USD - spread_usd)
F_compra <- calc_forward(r_COP - spread_cop, r_USD + spread_usd)
dentro <- F_compra <= FORWARD_SETFX & FORWARD_SETFX <= F_venta
cat(sprintf("Venta de USD (la empresa compra) : %s COP/USD\n", miles(F_venta, 2)))## Venta de USD (la empresa compra) : 3.446,55 COP/USD
## Compra de USD : 3.399,56 COP/USD
cat(sprintf("Forward observado (Bloomberg FRD) : %s COP/USD, dentro de la banda %s-%s? %s\n",
miles(FORWARD_SETFX, 2), miles(F_compra, 2), miles(F_venta, 2), dentro))## Forward observado (Bloomberg FRD) : 3.399,9 COP/USD, dentro de la banda 3.399,56-3.446,55? TRUE
Tabla B.1. Forward teórico, banda por spread y comparación con el mercado
tabla_b1 <- data.frame(
Concepto = c("Factor de paridad", "Forward teórico (F)", "Venta de USD (banda alta)",
"Compra de USD (banda baja)", "Forward observado Bloomberg (SET-FX)", "Prima anual implícita"),
Valor = c(round(factor_paridad_b1, 6), round(F_teorico, 2), round(F_venta, 2),
round(F_compra, 2), FORWARD_SETFX, paste0(round(prima_anual*100, 2), " %"))
)
dt_tabla(tabla_b1, caption = "Forward teórico COP/USD")r_cop_seq <- seq(0.09, 0.1501, by = 0.0025)
df_sens <- data.frame(r_cop = r_cop_seq, fwd = sapply(r_cop_seq, calc_forward, ru = r_USD))
g_b1 <- ggplot(df_sens, aes(x = r_cop * 100, y = fwd)) +
geom_line(color = AZUL, linewidth = 1.3) +
geom_point(color = AZUL, size = 1.8) +
geom_vline(xintercept = r_COP * 100, color = NARANJA, linetype = "dashed") +
labs(title = "Gráfico B.1. Forward teórico COP/USD vs. tasa en pesos (r_USD fija en 4.18 %)",
x = "Tasa en pesos, r_COP (%)", y = "Forward teórico (COP/USD)") +
theme_minimal()
ggplotly(g_b1)Cada uno de los cuatro forwards se pacta al cierre de los años 6, 7, 8 y 9 del crédito, usando el spot simulado de A.3 en ese momento y el factor de paridad de B.1, vence un año después, es decir años 7, 8, 9 y 10. El nominal de cada forward es el 75 % de la cuota anual del crédito (16,100.93 USD), es decir 12,075.70 USD por año.
| Forward (año) | Forward pactado (mediana) | Spot al vencimiento (mediana) | Probabilidad de protección | Rango del forward (p5–p95) |
|---|---|---|---|---|
| 1 (año 7) | 3,750.31 | 3,491.96 | 25.6% | 3,011.69 – 4,592.62 |
| 2 (año 8) | 3,754.08 | 3,505.49 | 26.1% | 3,017.52 – 4,594.91 |
| 3 (año 9) | 3,768.62 | 3,505.52 | 25.5% | 3,015.04 – 4,603.32 |
| 4 (año 10) | 3,768.66 | 3,505.13 | 25.3% | 3,028.54 – 4,607.22 |
Los cuatro forwards dan resultados muy parecidos, como A.3 es un modelo de reversión a la media, para los años 6 a 10 el spot ya converge casi por completo a su nivel de largo plazo, así que el nivel en que se pacta cada forward apenas cambia de un año a otro.
| Escenario | Spot al pactar | Forward pactado | Resultado del forward |
|---|---|---|---|
| Apreciación del peso | 3,488.46 | 3,750.31 | −943,48 (costo de oportunidad) |
| Central (mediana) | 3,488.46 | 3,750.31 | −258,35 (costo de oportunidad) |
| Dólar se dispara | 3,488.46 | 3,750.31 | +523,78 (protección) |
Una vez pactado el forward, ese precio ya no se mueve; lo que sí se mueve es el spot al que llegamos en el vencimiento. Y como ese spot tiende a quedarse cerca de su nivel de largo plazo, mientras que el forward ya trae incorporada la prima de 7.51 % anual de B.1, en la mayoría de los caminos el forward termina quedando más caro que el spot mediano. Solo cuando el dólar se dispara de verdad el forward resulta conveniente y sale más barato que haber comprado al spot.
| Escenario | Media, M COP | Desv. estándar, M COP |
|---|---|---|
| Sin cobertura, trayectorias de A.3 | 226.7 | 23.1 |
| Con cobertura 75 % años 7-10, trayectorias de A.3 | 239.2 | 23.9 |
| Sin cobertura, sensibilidad t-Student, 5 g.l. | 234.7 | 26.4 |
| Con cobertura 75 % años 7-10, sensibilidad t-Student | 247.4 | 27.5 |
La cobertura sube el costo esperado, 12.5 M bajo el modelo principal y 12.7 M bajo la sensibilidad de colas pesadas, porque el forward incorpora la prima de paridad de B.1 y el spot simulado de A.3 revierte casi siempre a un nivel por debajo de esa prima. La desviación estándar casi no cambia con la cobertura, 23.1 frente a 23.9 M, porque el forward solo cubre uno de cada cuatro años relevantes y, dentro de esos años, solo el 75 % de cada cuota, la dispersión que domina el costo total viene de los primeros seis años, que nunca se cubren.
| Tipo de escenario | Trayectorias de A.3 | Sensibilidad t-Student |
|---|---|---|
| Protección, spot mayor al forward | 25.6 % | 26.9 % |
| Costo de oportunidad, spot menor o igual al forward | 74.4 % | 73.1 % |
| Pérdida cambiaria extrema sin cobertura, depreciación mayor al 25 % en el año | 1.2 % | 2.9 % |
| Fracaso de la cobertura, costo cubierto mayor al forward más 5 % | 0.4 % | 1.5 % |
| Para referencia, ese mismo exceso sin cobertura | 12.4 % | 14.5 % |
Pérdida del derivado frente a fracaso de la cobertura. El forward paga menos que el spot en cerca de tres de cada cuatro escenarios, 74.4 %, otra vez por el costo de la prima de B.1, el spot revierte a un nivel más bajo que el forward con mucha más frecuencia de la que lo supera. Pero el presupuesto solo se sale seriamente de control en apenas el 0.4 % de los caminos, frente al 12.4 % de exceso si no se hubiera cubierto nada, la cobertura casi siempre implica pagar la prima, pero cuando aparece una depreciación grande recorta el daño de forma marcada. Con la sensibilidad t-Student, tanto la pérdida extrema sin cobertura, 2.9 % frente a 1.2 %, como el fracaso de la cobertura, 1.5 % frente a 0.4 %, más que se duplican, aunque la probabilidad de protección cambia poco, 26.9 % frente a 25.6 %, el riesgo que el modelo de A.3 puede estar subestimando es el de eventos extremos, no el del escenario típico.
nu_t <- 5
set.seed(SEMILLA)
Z_t_raw <- matrix(rt(N * MESES, df = nu_t), nrow = N, ncol = MESES) * sd(e) * sqrt((nu_t - 2) / nu_t)
calibrar_t <- function(Z, kappa) {
escala <- 1.0; nivel <- a_coef / (1 - PHI)
for (it in 1:25) {
s12 <- log(simular(Z, escala, nivel, kappa, 12)[, 13])
p5 <- quantile(s12, 0.05); p50 <- quantile(s12, 0.50); p95 <- quantile(s12, 0.95)
escala <- escala * (log(P95_A1 / P5_A1) / (p95 - p5))
nivel <- nivel + (log(MEDIANA_A1) - p50) / (1 - (1 - kappa)^12)
}
c(escala = unname(escala), nivel = unname(nivel))
}
cal_t <- calibrar_t(Z_t_raw, kappa)
S_t <- simular(Z_t_raw, cal_t["escala"], cal_t["nivel"], kappa) # N x 121, sensibilidad t-Student
forwards_idx <- 1:4
meses_pactar <- 12 * (5 + forwards_idx) # fin de los años 6,7,8,9 -> mes 72,84,96,108
meses_venc <- 12 * (6 + forwards_idx) # fin de los años 7,8,9,10 -> mes 84,96,108,120
calc_metrics_b2 <- function(S) {
prot_all <- c(); dep_all <- c(); fail_hedge_all <- c(); fail_nohedge_all <- c()
costo_sin <- numeric(nrow(S)); costo_con <- numeric(nrow(S))
filas <- list()
for (k in seq_along(forwards_idx)) {
mp <- meses_pactar[k]; mv <- meses_venc[k]
sp_pactar <- S[, mp + 1]; sp_venc <- S[, mv + 1]
fwd <- sp_pactar * factor_paridad_b1
prot <- sp_venc > fwd
prot_all <- c(prot_all, prot)
dep_all <- c(dep_all, (sp_venc / sp_pactar - 1) > 0.25)
fail_hedge_all <- c(fail_hedge_all, sp_venc > 1.20 * fwd)
fail_nohedge_all <- c(fail_nohedge_all, sp_venc > 1.05 * fwd)
costo_sin <- costo_sin + cuota_anual * sp_venc
costo_con <- costo_con + cuota_anual * (0.75 * fwd + 0.25 * sp_venc)
filas[[k]] <- data.frame(Forward = forwards_idx[k], Anio = 6 + forwards_idx[k],
fwd_mediana = median(fwd), spot_mediana = median(sp_venc),
prot_pct = mean(prot) * 100,
fwd_p5 = quantile(fwd, 0.05), fwd_p95 = quantile(fwd, 0.95))
}
list(tabla = do.call(rbind, filas),
prot_pct = mean(prot_all) * 100, dep_extrema_pct = mean(dep_all) * 100,
fail_hedge_pct = mean(fail_hedge_all) * 100, fail_nohedge_pct = mean(fail_nohedge_all) * 100,
costo_sin_mean = mean(costo_sin) / M, costo_sin_sd = sd(costo_sin) / M,
costo_con_mean = mean(costo_con) / M, costo_con_sd = sd(costo_con) / M)
}
m_emp <- calc_metrics_b2(S)
m_t <- calc_metrics_b2(S_t)Tabla B.2.1. Forward pactado frente al spot al vencimiento (trayectorias de A.3)
Tabla B.2.2. Costo combinado del 75 % cubierto y el 25 % abierto (M COP)
costo_combinado <- data.frame(
Escenario = c("Sin cobertura (trayectorias A.3)", "Con cobertura 75% años 7-10 (trayectorias A.3)",
"Sin cobertura (sensibilidad t-Student)", "Con cobertura 75% años 7-10 (sensibilidad t-Student)"),
Media_M_COP = c(m_emp$costo_sin_mean, m_emp$costo_con_mean, m_t$costo_sin_mean, m_t$costo_con_mean),
Desv_M_COP = c(m_emp$costo_sin_sd, m_emp$costo_con_sd, m_t$costo_sin_sd, m_t$costo_con_sd)
)
dt_tabla(costo_combinado, dec = 1, caption = "Costo combinado 75/25")Tabla B.2.3. Probabilidades de protección, costo de oportunidad y fracaso de la cobertura
tabla_prob <- data.frame(
Tipo = c("Protección (spot > forward)", "Costo de oportunidad (spot <= forward)",
"Pérdida cambiaria extrema sin cobertura (depreciación > 25% en el año)",
"Fracaso de la cobertura (costo cubierto > forward + 5%)",
"Para referencia: ese mismo exceso sin cobertura"),
Trayectorias_A3 = c(m_emp$prot_pct, 100 - m_emp$prot_pct, m_emp$dep_extrema_pct,
m_emp$fail_hedge_pct, m_emp$fail_nohedge_pct),
Sensibilidad_tStudent = c(m_t$prot_pct, 100 - m_t$prot_pct, m_t$dep_extrema_pct,
m_t$fail_hedge_pct, m_t$fail_nohedge_pct)
)
dt_tabla(tabla_prob, dec = 1, caption = "Probabilidades de protección y fracaso")df_b2 <- m_emp$tabla
df_b2$spot_p5 <- sapply(meses_venc, function(mv) quantile(S[, mv + 1], 0.05))
df_b2$spot_p95 <- sapply(meses_venc, function(mv) quantile(S[, mv + 1], 0.95))
g_b2 <- ggplot(df_b2, aes(x = Anio)) +
geom_ribbon(aes(ymin = spot_p5, ymax = spot_p95), fill = VERDE, alpha = 0.2) +
geom_line(aes(y = spot_mediana), color = VERDE, linewidth = 1.1) +
geom_point(aes(y = spot_mediana), color = VERDE, size = 2) +
geom_line(aes(y = fwd_mediana), color = AZUL, linewidth = 1, linetype = "dashed") +
geom_point(aes(y = fwd_mediana), color = AZUL, size = 2, shape = 15) +
labs(title = "Gráfico B.2. Forward pactado vs. rango simulado del spot (trayectorias de A.3), años 7 a 10",
x = "Año del crédito", y = "COP/USD") +
theme_minimal()
ggplotly(g_b2)Se inicia el 2 de septiembre de 2026, el día en que se compra la maquinaria, se pone la plata propia y se desembolsa el préstamo; ese mismo día el saldo neto queda en cero. De ahí en adelante, las 40 cuotas trimestrales en dólares se van pasando a pesos con el tipo de cambio simulado de cada trimestre durante los años 1 a 6. Ya en los años 7 a 10, donde entra la cobertura, cada cuota se paga 75% con el futuro pactado y el 25% restante al contado. Usamos la misma simulación de B.2, pero ahora trimestral en vez de anual, para tener una conversión al contado en cada una de las 40 cuotas; los cuatro futuros se siguen cotizando igual que en B.1, con la paridad aplicada sobre el tipo de cambio vigente al arrancar cada año cubierto.
| Escenario | Media | Desv. estándar | Percentil 5 | Percentil 95 |
|---|---|---|---|---|
| Sin cobertura | 666M | 140.5M | 467.1M | 920.9M |
| Con cobertura 75 % años 7–10 (normal) | 675.7M | 141.3M | 475.4M | 931.7M |
| Sin cobertura (t-Student) | 666M | 141.2M | 467.4M | 919.4M |
| Con cobertura 75 % años 7–10 (t-Student) | 675.7M | 142M | 475.4M | 930.3M |
La cobertura sube el costo esperado en 1.46%, y casi no reduce la desviación estándar de todo el flujo. Esto pasa porque los primeros 24 trimestres quedan sin cobertura, y dentro de los años sí cubiertos, el 75% está atado a un futuro cuyo nivel igual depende de cómo se haya movido el tipo de cambio hasta ese momento, así que la cobertura no rompe la correlación entre el costo de esos años y el resto de la trayectoria. Sí reduce la dispersión dentro de cada año cubierto, pero todo el crédito sigue expuesto en sus primeros seis años, que es donde se concentra la mayor parte de la incertidumbre acumulada.
| Escenario | Costo total sin cobertura | Costo total con cobertura | Spot final (año 10) |
|---|---|---|---|
| Favorable (peso fuerte, p5) | 467.1M | 471.5M | 3,095.62 |
| Central (mediana) | 649.4M | 675.5M | 3,279.14 |
| Adverso (depreciación, p95) | 920.9M | 942.4M | 7,191.82 |
En el escenario central, cubrirse sale más caro que no hacerlo, porque el peso se deprecia menos del 7.5% anual que ya trae incorporado el futuro; la cobertura solo baja el costo total si la depreciación se mantiene por encima de ese umbral durante los años 7 a 10 de forma sostenida, y en este modelo esos casos quedan en la cola derecha de la distribución, no en el escenario central.
La cobertura cuesta en promedio 1.46% más en los tres niveles de confianza y tampoco baja mucho la volatilidad del flujo total porque solo cubre el 30% del crédito nominal. Donde sí se nota es en la cola, en el percentil 99 del costo cubierto, el exceso sobre el futuro pactado es de solo 6-7%, frente a 24-28% sin cobertura, la cobertura no cambia el nivel esperado del flujo, pero reduce el riesgo de un desembolso extremo en los años 7 a 10.
El flujo es volátil porque los años 1 a 6 quedan abiertos, y porque el 25% restante de los años 7 a 10 también sigue expuesto al tipo de cambio. El riesgo residual de la estrategia termina siendo la mayor parte, el 60% del crédito nunca se cubre, y del 40% restante solo se cubren el 75%, es decir, el nominal cubierto equivale al 30% de la obligación en dólares. Ese 75% aplica solo al tramo de los años 7 a 10, no a todo el crédito, así que el nominal cubierto nunca supera la obligación real.
Ese costo de oportunidad promedio es básicamente lo que pagas a cambio de tener protección en la cola, y no es otra cosa que la prima anual del futuro de B.1 aplicada sobre el nominal cubierto. Como ya vimos en B.1, 100 puntos básicos más en la tasa del peso o 100 menos en la del dólar mueven ese costo casi igual que el margen que cobra el banco, así que el resultado depende bastante de que asumamos que la tasa de política es lo mismo que el IBR a 12 meses.
valor_maquinaria <- 400000000
aporte_propio <- 0.10 * valor_maquinaria
desembolso_cop <- 0.90 * valor_maquinaria
desembolso_usd <- desembolso_cop / S0
saldo_neto_t0 <- valor_maquinaria - aporte_propio - desembolso_cop
cat(sprintf("Compra de maquinaria : %s COP\n", miles(valor_maquinaria)))## Compra de maquinaria : 400.000.000 COP
## Aporte propio (10%) : 40.000.000 COP
cat(sprintf("Desembolso crédito 90%% : %s COP (%s USD)\n", miles(desembolso_cop), miles(desembolso_usd, 2)))## Desembolso crédito 90% : 360.000.000 COP (113.065,3 USD)
## Saldo neto en t=0 : 0 COP
mediana_A3_12m <- 3326; p5_A3_12m <- 2795; p95_A3_12m <- 3953
mu_b2 <- log(mediana_A3_12m / S0)
z95 <- qnorm(0.95)
sigma_b2 <- mean(c(log(p95_A3_12m / mediana_A3_12m) / z95, log(p5_A3_12m / mediana_A3_12m) / (-z95)))
N_b3 <- 200000; TRIMESTRES <- 40; nu_b3 <- 5
mu_trim <- mu_b2 / 4; sigma_trim <- sigma_b2 / 2
sim_quarterly <- function(seed, dist) {
set.seed(seed)
Zq <- if (dist == "normal") matrix(rnorm(N_b3 * TRIMESTRES), N_b3, TRIMESTRES)
else matrix(rt(N_b3 * TRIMESTRES, df = nu_b3), N_b3, TRIMESTRES) * sqrt((nu_b3 - 2) / nu_b3)
logret <- mu_trim + sigma_trim * Zq
S0 * exp(t(apply(logret, 1, cumsum)))
}
Sq_n <- sim_quarterly(123, "normal")
Sq_t <- sim_quarterly(123, "t")
costos_b3 <- function(Sq) {
fwd1 <- Sq[, 24] * factor_paridad_b1; fwd2 <- Sq[, 28] * factor_paridad_b1
fwd3 <- Sq[, 32] * factor_paridad_b1; fwd4 <- Sq[, 36] * factor_paridad_b1
costo_sin <- PMT * rowSums(Sq)
costo_con <- PMT * rowSums(Sq[, 1:24])
costo_con <- costo_con + PMT * (4 * 0.75 * fwd1 + 0.25 * rowSums(Sq[, 25:28]))
costo_con <- costo_con + PMT * (4 * 0.75 * fwd2 + 0.25 * rowSums(Sq[, 29:32]))
costo_con <- costo_con + PMT * (4 * 0.75 * fwd3 + 0.25 * rowSums(Sq[, 33:36]))
costo_con <- costo_con + PMT * (4 * 0.75 * fwd4 + 0.25 * rowSums(Sq[, 37:40]))
list(costo_sin = costo_sin, costo_con = costo_con, spot_final = Sq[, 40])
}
res_n <- costos_b3(Sq_n); res_t <- costos_b3(Sq_t)
resumen_b3 <- function(costo) c(media = mean(costo) / M, sd = sd(costo) / M,
p5 = quantile(costo, 0.05) / M, p95 = quantile(costo, 0.95) / M)
tabla_costo_b3 <- rbind(
"Sin cobertura (normal)" = resumen_b3(res_n$costo_sin),
"Con cobertura 75% años 7-10 (normal)" = resumen_b3(res_n$costo_con),
"Sin cobertura (t-Student)" = resumen_b3(res_t$costo_sin),
"Con cobertura 75% años 7-10 (t-Student)" = resumen_b3(res_t$costo_con)
)
dif_pct_b3 <- resumen_b3(res_n$costo_con)["media"] / resumen_b3(res_n$costo_sin)["media"] - 1
cat(sprintf("Sobrecosto esperado de la cobertura (normal): %+.2f%%\n", dif_pct_b3 * 100))## Sobrecosto esperado de la cobertura (normal): +1.46%
Tabla B.3.1. Costo total en pesos del crédito, sin y con cobertura (M COP)
orden_b3 <- order(res_n$costo_sin)
idx_p5 <- orden_b3[round(0.05 * N_b3)]
idx_p50 <- orden_b3[round(0.50 * N_b3)]
idx_p95 <- orden_b3[round(0.95 * N_b3)]
tabla_esc_b3 <- data.frame(
Escenario = c("Favorable (peso fuerte, p5)", "Central (mediana)", "Adverso (depreciación, p95)"),
Costo_sin_M = c(res_n$costo_sin[idx_p5], res_n$costo_sin[idx_p50], res_n$costo_sin[idx_p95]) / M,
Costo_con_M = c(res_n$costo_con[idx_p5], res_n$costo_con[idx_p50], res_n$costo_con[idx_p95]) / M,
Spot_final = c(res_n$spot_final[idx_p5], res_n$spot_final[idx_p50], res_n$spot_final[idx_p95])
)
nominal_cubierto_pct <- 0.75 * (16 / 40)
cat(sprintf("Nominal cubierto como %% de la obligación total: %.2f%%\n", nominal_cubierto_pct * 100))## Nominal cubierto como % de la obligación total: 30.00%
cat(sprintf("Trimestres sin ningún tipo de cobertura (años 1-6): 24 de 40 (%.0f%%)\n", 24 / 40 * 100))## Trimestres sin ningún tipo de cobertura (años 1-6): 24 de 40 (60%)
Tabla B.3.2. Tres escenarios representativos
g_b3 <- ggplot(data.frame(costo_M = res_n$costo_sin / M), aes(x = costo_M)) +
geom_histogram(bins = 70, fill = GRIS, alpha = 0.6, color = "white") +
geom_histogram(data = data.frame(costo_M = res_n$costo_con / M), aes(x = costo_M),
bins = 70, fill = AZUL, alpha = 0.45, color = "white") +
geom_vline(xintercept = mean(res_n$costo_sin)/M, color = GRIS, linetype = "dashed") +
geom_vline(xintercept = mean(res_n$costo_con)/M, color = AZUL, linetype = "dashed") +
labs(title = "Gráfico B.3. Costo total del crédito en pesos, sin cobertura (gris) vs. con cobertura (azul)",
subtitle = "Modelo log-normal trimestral independiente, calibrado a A.1",
x = "Costo total (millones de COP)", y = "Escenarios") +
theme_minimal()
ggplotly(g_b3)Apple Inc. (AAPL), The Coca-Cola Company (KO) y Johnson & Johnson (JNJ), La elección de estas acciones no se basa en la popularidad de las marcas, sino en diversificación sectorial reduciendo así la correlación entre los activos y a su vez esto mejora la frontera eficiente, elegimos sectores como la tecnología, el consumo básico y salud que corresponden a ciclos económicos totalmente distintos, además de esto cabe recalcar que las tres empresas cotizan hace más de 10 años en el mercado, sin salidas a bolsas recientes ni cambio de ticker, adicionalmente son empresas de gran capitalización y con un alto volumen diario por lo que nos permite invertir 20 millones sin afectar el precio, tienen flujos de caja estables y décadas de dividendos crecientes como lo son coca cola y j&j.
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Apple es una empresa que diseña y vende dispositivos electrónicos de consumo como: iPhone, Mac, iPad, Apple Watch, AirPods y adicional tiene un ecosistema de servicios digitales como: App Store, iCloud, Apple Music, Apple Pay, AppleCare, publicidad y acuerdos de licencia. Pertenece al sector de Tecnología, subindustria de hardware y equipos de tecnología. Cuenta con una capitalización cercana a 5 billones de dólares y es una de las empresas de mayor peso en el S&P 500 por lo que su comportamiento está muy ligado al del índice.
Referencia: Fuente: Apple, formulario 10-Q del trimestre terminado el 27-jun-2026.
Aproximadamente la mitad de los ingresos de Apple vienen del iPhone, por lo que esta parte puede variar más. En cambio, los Servicios tienen mejores márgenes y ya representan más de una cuarta parte de los ingresos, además de seguir creciendo. Esto hace que Apple tenga ingresos más estables y ayuda a explicar por qué la acción tiene una valoración alta.
Entre 2016 y 2025, la acción de Apple pasó de aproximadamente 29 a 271 dólares. Esto representa un crecimiento promedio de cerca del 28% anual. Sin embargo, no siempre la acción se mantuvo en crecimiento, también tuvo algunas caídas importantes, especialmente en 2018 QUE BAJO CERCA DE UN 7% y 2022 cuando bajó alrededor de un 27%. En 2026 la acción lleva una subida cercana al 18% y cerró en 319,70 dólares el 28 de agosto. El 31 de julio tuvo una caída fuerte de 7,4% en un solo día, después de presentar resultados en los que los ingresos por Servicios fueron menores a lo esperado. Durante agosto logró recuperar parte de esa caída, pasando de unos 303 a 320 dólares mientras el mercado esperaba el lanzamiento del iPhone 18 en septiembre.
Para entender la situación financiera de Apple hay que tener en cuenta que la empresa tiene un año fiscal diferente al año calendario, el año fiscal de Apple empieza en septiembre del año anterior y termina en septiembre del año actual, no como las empresas convencionales que su año fiscal empieza en enero, sabiendo todo esto, Apple en los primeros nueve meses del año fiscal 2026 tuvo ingresos de 364.357 millones de dólares, lo que refleja una utilidad neta de 101.464 millones de dólares y generó 116.996 millones de dólares en flujo de caja operativo. Esto nos demuestra que la empresa sigue teniendo una fuerte capacidad para generar ingresos y efectivo. En el último trimestre, el margen bruto fue de 50,1%. Sin embargo, la empresa
Se espera que este margen baje a entre 47% y 48% en el trimestre de septiembre por el aumento en los costos de las memorias y del procesamiento necesario para la inteligencia artificial. Apple también mantiene una posición financiera sólida ya que cuenta con cerca de 147.000 millones de dólares entre efectivo e inversiones, frente a una deuda de aproximadamente 85.000 millones de dólares, Esto deja una caja neta cercana a USD 62.000 millones reflejándose que la empresa tiene más efectivo e inversiones que deuda. Por otro lado, Apple continúa repartiendo dinero a sus accionistas. La empresa anunció un nuevo programa de recompra de acciones por 100.000 millones de dólares y aumentó su dividendo trimestral a 0,27 dólares representando un aumento del 4%. Con estos resultados la acción tiene una valoración bastante alta ya que El PER histórico está alrededor de 37 a 39 veces mientras que el PER esperado se encuentra entre 33 a 35 veces. Mostrándonos unos niveles que están por encima del promedio que ha tenido Apple durante los últimos diez años.
Después de analizar todo, consideramos que en el corto plazo la acción tiene un potencial de subida más limitado, porque su precio actual ya está bastante cerca del precio objetivo promedio de los analistas. Sin embargo, si se mira a un plazo de unos cuatro años, Apple todavía tiene buenas perspectivas, principalmente por el crecimiento de sus ingresos, sus márgenes altos y las recompras de acciones que está haciendo. Estas recompras también pueden ayudar a que la utilidad por acción siga aumentando.
Apple es la empresa que más aporta a la rentabilidad del portafolio, ya que históricamente ha tenido el mayor retorno de las tres. Esto hace que la frontera eficiente del portafolio se desplace hacia arriba. Sin embargo, también tiene una mayor volatilidad y una beta más alta, lo que representa el riesgo sistemático que se busca cubrir con futuros del S & P 500. Además, como
Apple tiene una alta correlación con el índice, la cobertura puede ser más efectiva y tener un menor riesgo de base. Por su múltiplo elevado, se recomienda que su peso no sea demasiado alto dentro del portafolio, aunque esto se terminará definiendo con las restricciones del modelo media-varianza.
Coca Cola es la empresa de bebidas no alcohólicas más grande del mundo y cuenta con más de 200 marcas, entre ellas Coca Cola, Coca Cola Zero Sugar, Sprite, Fanta, Dasani, Powerade, Fairlife y Minute Maid. Sus productos se venden en más de 200 países. La empresa pertenece al sector de Consumo Básico específicamente al de bebidas donde la demanda suele mantenerse estable incluso cuando hay periodos de recesión.
El modelo de negocio de Coca Cola se basa en tener pocos activos propios, La empresa vende concentrados y jarabes a una red de embotelladores independientes entre ellos Coca Cola FEMSA en Latinoamérica que se encargan de producir, distribuir y vender los productos. Gracias a este modelo, sus márgenes operativos superan el 30%. Los ingresos se distribuyen entre diferentes regiones como Norteamérica, Latinoamérica, Europa, Medio Oriente, África y Asia, además de las operaciones de embotellado propias y Global Ventures.
Fuente: Coca-Cola, comunicado de resultados del segundo trimestre de 2026 (28-jul-2026).
Entre 2016 y 2025 la acción pasó de aproximadamente 41 a 69 dólares lo que representa un crecimiento de cerca del 6% anual en el precio sin contar los dividendos. Durante este periodo tuvo caídas moderadas incluso entre 2020 y 2022 que entramos en pandemia, lo que nos demuestra que es una acción más defensiva. En 2026 ha tenido un desempeño bastante bueno, con una subida cercana al 29% y un cierre de 89,66 dólares el 28 de agosto. Además, llegó a 91,99 dólares el 24 de agosto, quedando por encima del S&P 500 y de sus competidores durante el mismo periodo. Este aumento se relaciona con el crecimiento en el volumen de ventas, el buen comportamiento de las bebidas sin azúcar y el impulso comercial que ha tenido la empresa por el Mundial FIFA 2026.
Para 2026 Coca Cola espera un crecimiento orgánico de los ingresos de aproximadamente 5% y un crecimiento de la UPA comparable de entre 9% y 10%. Además, se espera que el flujo de caja libre llegue a unos 12.400 millones de dólares durante el año, de los cuales ya llevaba 6.900 millones de dólares acumulados hasta junio. La empresa también tiene un dividendo trimestral de 0,53 dólares que equivale a 2,12 dólares al año y representa un rendimiento cercano al 2,4%. Además, lleva más de 60 años aumentando el dividendo de manera consecutiva. Entre los principales riesgos están el aumento en los costos de los insumos, la competencia de las marcas propias y las regulaciones relacionadas con el azúcar. También está el efecto de la tasa de cambio, ya que la mayoría de las ventas de Coca Cola se realizan fuera de Estados Unidos.
Coca-Cola funciona como un estabilizador dentro del portafolio, ya que su beta baja y su poca relación con Apple ayudan a reducir la varianza total y el VaR. Además, tiene un dividendo que es relativamente predecible por lo que debe tenerse en cuenta al calcular el rendimiento y al hacer la valoración teórica del futuro. Se espera un retorno total moderado de aproximadamente 8% entre la valorización de la acción y el dividendo, lo cual es consistente con su nivel de riesgo más bajo. Sin embargo, después de que la acción subiera 29% en 2026, el precio de entrada resulta menos atractivo que en años anteriores.
Johnson & Johnson es una de las compañías de salud más grandes del mundo. Desde que separó su negocio de consumo Kenvue en 2023 la empresa se enfoca en dos segmentos principales: El primero es Medicina Innovadora que corresponde al negocio farmacéutico, y el segundo es MedTech que incluye dispositivos médicos para áreas como cirugía, ortopedia, cardiovascular y visión. La empresa pertenece al sector Salud, donde la demanda suele verse poco afectada por los cambios del ciclo económico.
El crecimiento viene principalmente de oncología con productos como Darzalex (+17,6%), Carvykti (+47,7%), Tecvayli y Rybrevant, y de inmunología, donde Tremfya superó por primera vez los USD 2.000 millones en ventas trimestrales. Esto ayuda a compensar la caída de Stelara por la entrada de biosimilares, que redujo el crecimiento en cerca de 7,6 puntos porcentuales.
Entre 2016 y 2024 la acción tuvo un comportamiento más bien estable pasando de 115 a 145 dólares. Durante este periodo estuvo afectada por los problemas legales relacionados con el talco y por la incertidumbre alrededor de Stelara, sin embargo desde 2025 la situación cambió, la acción cerró ese año cerca de 204 dólares y en 2026 acumula una subida cercana al 32% Llegando a 273 dólares el 25 de agosto y cerró en 268,04 dólares el 28 de agosto, relacionando esta subida con que la empresa ha logrado compensar la pérdida de exclusividad de Stelara y con los buenos resultados de sus nuevos productos.
Para 2026, la empresa espera ventas de 101.100 millones de dólares lo que representa un crecimiento del 7,3%, y una UPA ajustada de 11,68 dólares que crecería un 8,2%. También se espera un flujo de caja libre cercano a 21.000 millones de dólares y la empresa tiene una deuda total de 49.000 millones de dólares frente a una caja de 20.400 millones de dólares. Además, es una de las pocas compañías que cuenta con una calificación crediticia AAA, cuenta con un dividendo trimestral es de 1,34 dólares, que equivale a 5,36 dólares al año y representa un rendimiento cercano al 2,0%. Johnson & Johnson lleva 64 años aumentando su dividendo de forma consecutiva y tiene un payout cercano al 48%, Por otro lado, su PER está alrededor de 18 a 23 veces, siendo este un nivel menor al de Apple.
El principal riesgo sigue siendo los litigios relacionados con el talco que todavía están abiertos. A esto se suman la presión sobre los precios de los medicamentos en Estados Unidos y los posibles efectos de los aranceles.
J&J aporta crecimiento defensivo al portafolio ya que tiene una beta baja, pero al mismo tiempo cuenta con nuevas oportunidades de crecimiento gracias a su negocio farmacéutico, Su correlación con Apple y Coca-Cola es baja porque sus ingresos dependen principalmente de la aprobación y el uso de medicamentos y no tanto del consumo o del ciclo tecnológico. Por eso ayuda a diversificar el portafolio y a que este dependa menos del comportamiento general del mercado. El riesgo relacionado con los litigios es propio de la empresa, no es un riesgo sistemático por lo que puede reducirse al combinar J&J con otras inversiones. Sin embargo, este riesgo no se cubre con el futuro del índice.
datos <- read_excel("datos.xlsx")
datos <- as.data.frame(datos)
datos$Date <- as.Date(datos$Date)
ret <- data.frame(
Date = datos$Date[-1],
raapl = diff(log(datos$aapl)),
rjnj = diff(log(datos$jnj)),
rko = diff(log(datos$ko)),
rgspc = diff(log(datos$gspc))
)
varp <- function(x) mean((x - mean(x))^2)
covarp <- function(x, y) mean((x - mean(x)) * (y - mean(y)))
sdp <- function(x) sqrt(varp(x))
dias <- 252Tabla P2.0. Primeras y últimas observaciones de precios (AAPL, JNJ, KO, S&P 500 y futuro)
Se trabajó con una frecuencia diaria, usando los precios de cierre de Apple, Johnson & Johnson y Coca-Cola desde el 30 de agosto de 2016 hasta el 28 de agosto de 2026. Esta última fecha corresponde al último día hábil antes de comenzar la inversión, que sería el 30 de agosto de 2026. En total se tienen 10 años de datos dentro del rango de 6 a 10 años solicitado con 2.513 precios y 2.512 retornos diarios para cada acción.
Se escogió la frecuencia diaria porque permite tener una mayor cantidad de datos y, por lo tanto, hacer una estimación más confiable de la volatilidad y las covarianzas. Además, esta frecuencia coincide con la forma en que se ajustan los futuros en la práctica mediante el mark-to-market. Además de esto los precios están ajustados por splits pero no incluyen dividendos por lo que los retornos calculados muestran únicamente la valorización del precio de cada acción.
Para cada día se calculó el retorno usando los precios de cierre de cada acción:
r(t) = LN(P(t) / P(t−1))
Se utilizó este tipo de retorno porque permite sumar los retornos diarios para obtener el retorno de un periodo más largo. Esto facilita hacer los cálculos anuales a partir de los datos diarios.
Para pasar los datos diarios a valores anuales se tomaron 252 días bursátiles, que es la cantidad que normalmente se usa en el mercado estadounidense y también la que nos indicaron tomar. Con esto, el retorno anual promedio se obtiene multiplicando el promedio diario x 252. Para obtener el retorno anual efectivo se usa EXP(promedio diario × 252) − 1. De la misma forma, la varianza y la covarianza anual se calculan multiplicando las diarias por 252, mientras que la desviación estándar se multiplica por √252. La desviación estándar se multiplica por √252 y no por 252 porque la varianza es la que aumenta con el paso del tiempo. Al sacar la raíz cuadrada de la varianza se obtiene la desviación estándar. Por otro lado, la correlación no necesita anualizarse porque no tiene una unidad específica y no cambia por usar datos diarios o anuales. Para los cálculos se usaron las varianzas y covarianzas poblacionales (VARP y COVAR).
Apple es claramente la acción con mayor retorno, con un 24,98% anual continuo y 28,38% efectivo, más de tres veces el retorno de las otras dos acciones. Sin embargo, también es la más riesgosa, con una desviación estándar anualizada de 29,13%. Esto significa que sus rendimientos han tenido cambios bastante fuertes y que el resultado de un año puede alejarse bastante de su promedio. Sus mayores movimientos en un solo día fueron de -13,8% el 16 de marzo de 2020, durante la crisis del COVID-19, y de +14,3% el 9 de abril de 2025.
Johnson & Johnson y Coca Cola tienen un comportamiento bastante parecido. Sus retornos anuales fueron de 8,11% y 7,32%, mientras que sus volatilidades fueron de 18,88% y 18,60%, respectivamente. Son acciones más estables y tienen cerca de dos tercios del riesgo de Apple, lo
que coincide con el carácter defensivo que se había planteado en el análisis fundamental. De todas formas, también han tenido días de caídas fuertes. J&J perdió 10,6% en un solo día el 14 de diciembre de 2018 por noticias relacionadas con el litigio del talco, mientras que Coca Cola cayó 10,2% el 12 de marzo de 2020.
Relación retorno/riesgo. Por cada punto de volatilidad asumido, Apple obtuvo 0,86 puntos de retorno, mientras que J&J obtuvo 0,43 y Coca Cola 0,39. Es decir, Apple tuvo un mayor retorno y también logró compensar mejor el riesgo que asumió.
La diagonal de la matriz muestra la varianza de cada acción. En el caso de Apple es de 0,0848, más del doble que la de J&J, que es 0,0356, y la de Coca Cola, que es 0,0346. Fuera de la diagonal todas las covarianzas son positivas lo que quiere decir que las tres acciones tienden a moverse en la misma dirección. Por ejemplo, cuando el mercado sube o baja las acciones también suelen moverse, aunque no exactamente en la misma proporción. Esta parte común representa el riesgo sistemático que no se puede eliminar completamente diversificando y es el que se busca cubrir con futuros sobre el S&P 500 y las covarianzas son relativamente pequeñas frente a las varianzas ya que están entre 0,016 y 0,018.
La correlación sirve para ver qué tanto se mueven dos acciones de manera parecida. Su valor va de −1 a 1, por lo que permite comparar fácilmente qué tan relacionadas están entre sí.
-Apple y JNJ (0,288) y Apple y Coca-Cola (0,327): tienen una correlación positiva pero baja, lo que significa que Apple se mueve de forma relativamente independiente de las otras dos acciones, por lo que combinarlas ayuda a reducir el riesgo del portafolio.
-JNJ y Coca-Cola (0,494): tienen una correlación moderada y es la más alta de las tres. Esto tiene sentido porque ambas son empresas defensivas y pueden reaccionar de manera parecida ante algunas noticias económicas.
-Ninguna correlación es negativa ni cercana a 1: esto significa que ninguna de las acciones funciona como una cobertura natural de otra, pero tampoco hay dos que se comportan casi igual. Por lo que podríamos decir que las tres aportan diversificación del portafolio.
R <- ret[, c("raapl", "rjnj", "rko")]
colnames(R) <- c("aapl", "jnj", "ko")
n_ret <- sapply(R, length)
prom_diar <- sapply(R, mean)
ret_anu_c <- prom_diar * dias
ret_anu_ef <- exp(prom_diar * dias) - 1
var_diar <- sapply(R, varp)
sd_diar <- sqrt(var_diar)
var_anu <- var_diar * dias
sd_anu <- sd_diar * sqrt(dias)
stats <- rbind(
"Número de retornos diarios" = n_ret,
"Retorno diario promedio" = prom_diar,
"Retorno anual promedio (continuo)" = ret_anu_c,
"Retorno anual efectivo" = ret_anu_ef,
"Varianza diaria" = var_diar,
"Desviación estándar diaria" = sd_diar,
"Varianza anual" = var_anu,
"Desviación estándar anualizada" = sd_anu
)
cov_diaria <- outer(1:3, 1:3, Vectorize(function(i, j) covarp(R[[i]], R[[j]])))
dimnames(cov_diaria) <- list(c("aapl", "jnj", "ko"), c("aapl", "jnj", "ko"))
cov_anual <- cov_diaria * dias
cor_mat <- cor(R)
dimnames(cor_mat) <- dimnames(cov_diaria)Tabla P2.1. Estadísticas de retorno y riesgo por acción
Tabla P2.2. Matriz de varianzas y covarianzas (anualizada)
Gráfico P2.1. Matriz de correlación entre AAPL, JNJ y KO
cor_long <- as.data.frame(as.table(cor_mat))
names(cor_long) <- c("accion1", "accion2", "correlacion")
g_cor <- ggplot(cor_long, aes(x = accion1, y = accion2, fill = correlacion,
text = paste0(accion1, " vs ", accion2, ": ", round(correlacion, 3)))) +
geom_tile(color = "white") +
geom_text(aes(label = round(correlacion, 3)), color = "white", fontface = "bold") +
scale_fill_gradient(low = "#c7d2e3", high = AZUL) +
labs(title = "Matriz de correlación", x = NULL, y = NULL, fill = "Correlación") +
theme_minimal()
ggplotly(g_cor, tooltip = "text")El modelo de media-varianza busca encontrar cómo repartir el capital entre las tres acciones para tener una buena relación entre el retorno esperado y el riesgo. Para esto utilizamos solver, cambiando los pesos de Apple, J&J y Coca Cola hasta encontrar la combinación que maximizara el ratio de sharpe. Además, establecimos dos restricciones:
-Los pesos deben sumar 100%, para invertir todo el capital.
-Cada acción debe tener un peso entre 10% y 40%.
Estas restricciones son necesarias porque si se deja al modelo sin ningún límite, este termina asignando el 100% a Apple, ya que históricamente es la acción que tiene la mejor relación entre retorno y riesgo, el problema es que eso haría que el portafolio quedara prácticamente concentrado en una sola acción, por eso el mínimo de 10% hace que las tres acciones tengan participación, mientras que el máximo de 40% evita que una sola termine teniendo demasiado peso.
Apple (40,00%): queda con el peso máximo permitido porque es la acción que tiene el mayor retorno por cada unidad de riesgo. De hecho, si el modelo no tuviera el límite del 40%, le asignaría un peso mayor.
J&J (37,63%): tiene un peso mayor que Coca Cola porque presenta un retorno un poco más alto de 8,44% frente a 7,59%, y además tiene una menor correlación con Apple que es de 0,288 frente a 0,327 de coca cola.
Coca Cola (22,37%): es la acción con menor retorno, pero aun así queda por encima del mínimo del 10%. Esto se debe principalmente a que su volatilidad es baja y ayuda a disminuir el riesgo total del portafolio.
En total, el 60% del portafolio queda invertido en J&J y Coca Cola ya que son las acciones más defensivas mientras que el 40% queda en Apple que tiene un mayor potencial de retorno, pero también más riesgo. Para calcular cuánto dinero se invierte en cada acción, se multiplica su peso por los USD 20.000.000 disponibles. Después el número de acciones se obtiene dividiendo ese monto entre el precio de cierre de cada acción del 28 de agosto de 2026.
El retorno esperado del portafolio es de 16,23% y se obtiene tomando los retornos de las tres acciones y teniendo en cuenta el peso de cada una. Si se aplica este porcentaje a los 20 millones de dólares invertidos, se tendría una ganancia esperada de aproximadamente 3,2 millones de dólares al año.
La desviación estándar de 17,67% representa el riesgo anual del portafolio. Para una inversión de 20 millones de dólares esto equivale aproximadamente a USD 3,5 millones hacia arriba o hacia abajo. Hay dos cosas que vale la pena mirar. Primero, el riesgo del portafolio es menor que el de cualquiera de las tres acciones por separado, Segundo, también es menor que el promedio ponderado de las volatilidades individuales que sería de 22,92%.
El Sharpe Ratio se calcula como 16,23% − 4,445% / 17,67% y da un resultado de 0,667. Esto quiere decir que por cada punto de riesgo que se asume el portafolio genera 0,667 puntos de retorno por encima de la tasa libre de riesgo.
Para la tasa libre de riesgo se utilizó un valor de 4,445%, que corresponde al promedio entre la tasa de los bonos del Tesoro de Estados Unidos a 3 años que es de 4,41% y a 5 años que es de 4,48% al 28 de agosto de 2026. Se tomó este promedio porque no se cuenta con una tasa específica para un plazo de 4 años, que es el periodo planteado para la inversión.
inver_total <- 20000000 # Inver total (USD)
min_w <- c(aapl = 0.1, jnj = 0.1, ko = 0.1)
max_w <- c(aapl = 0.4, jnj = 0.4, ko = 0.4)
w <- c(aapl = 0.4, jnj = 0.376333996568832, ko = 0.223666003431168) # opt weight (Solver)
inver <- w * inver_total
precio <- unlist(tail(datos[, c("aapl", "jnj", "ko")], 1))
cartera <- inver / precio
rf_3y <- 0.0441; rf_5y <- 0.0448
rf <- (rf_3y + rf_5y) / 2
ret_esp <- sum(w * ret_anu_ef)
var_port <- as.numeric(t(w) %*% cov_anual %*% w)
sd_port <- sqrt(var_port)
sharpe <- (ret_esp - rf) / sd_portTabla P2.3. Pesos óptimos y monto invertido
p3 <- rbind(
"Retorno anual efectivo" = ret_anu_ef,
"Desviación estándar anualizada" = sd_anu,
"Peso mínimo" = min_w,
"Peso máximo" = max_w,
"Peso óptimo (Solver)" = w,
"Inversión (USD)" = inver,
"Precio de cierre 28/08/2026 (USD)" = precio,
"Cartera (número de acciones)" = cartera
)
dt_tabla(as.data.frame(p3), dec = 4, caption = "Portafolio óptimo")Tabla P2.4. Resumen del portafolio
resumen <- data.frame(
Valor = c(ret_esp, var_port, sd_port, rf, sum(w), sharpe, sum(inver)),
row.names = c("Retorno esperado", "Varianza del portafolio", "Desviación estándar",
"Tasa libre de riesgo anual", "Peso total", "Sharpe Ratio",
"Inversión total asignada (USD)")
)
dt_tabla(resumen, dec = 6, caption = "Resumen del portafolio")El Valor en Riesgo nos permite estimar cuánto podría perder el portafolio en un periodo determinado teniendo en cuenta un nivel de confianza. En nuestro caso el cálculo se hizo sobre una inversión total de 20.000.000 de dólares tomando un horizonte de un mes y dos niveles de confianza: 95% y 99%. Esto corresponde al VaR al 5% y al VaR al 1%, respectivamente.
Para calcular el VaR se usaron los datos históricos del portafolio, se tomaron los precios de cierre de cada mes durante los últimos 10 años y con estos datos se calcularon los retornos mensuales de Apple, J&J y Coca Cola. Después se combinaron esos retornos teniendo en cuenta el peso de cada acción dentro del portafolio y se calculó cuánto se habría ganado o perdido cada mes sobre los 20 millones de dólares invertidos. Finalmente, se tomaron los peores resultados para calcular el VaR al 95% y al 99%.
Con un nivel de confianza del 95%, se estima que el portafolio no debería perder más de 6,84% de su valor en un mes. Esto equivale a una pérdida de aproximadamente 1,37 millones de dólares sobre los 20 millones de dólares invertidos. En términos sencillos, en alrededor de 5 de cada 100 meses la pérdida podría ser mayor a este valor. Por eso, este resultado representa un escenario de pérdida fuerte, pero que podría presentarse con cierta frecuencia.
Con un nivel de confianza del 99% la pérdida estimada en un mes no debería superar el 11,13% que serían aproximadamente 2,23 millones de dólares. En este caso, solo en cerca de 1 de cada 100 meses la pérdida podría ser mayor. Por eso, este resultado representa un escenario mucho más extremo, similar a lo que podría pasar durante un mes de crisis.
El VaR muestra que en un mes el portafolio podría llegar a perder entre 1,37 y 2,23 millones de dólares. Por eso tiene sentido usar futuros sobre el S&P 500 para proteger parte de la inversión. Sin embargo, el valor que se busca cubrir no es directamente esa posible pérdida. La cobertura se calcula sobre los 20 millones de dólares del portafolio y se ajusta según la beta para definir cuántos contratos se necesitan.
Además, los futuros ayudan a proteger principalmente frente a los movimientos generales del mercado mas no cubren los problemas propios de cada empresa. Por eso, aunque la cobertura puede reducir la pérdida que podría tener el portafolio no significa que el riesgo desaparezca por completo.
mes <- format(datos$Date, "%Y-%m")
fin_mes <- datos[!duplicated(mes, fromLast = TRUE), c("Date", "aapl", "jnj", "ko")]
fin_mes <- fin_mes[fin_mes$Date >= as.Date("2016-08-31"), ]
rownames(fin_mes) <- NULL
fin_mes$raapl <- c(NA, diff(log(fin_mes$aapl)))
fin_mes$rjnj <- c(NA, diff(log(fin_mes$jnj)))
fin_mes$rko <- c(NA, diff(log(fin_mes$ko)))
fin_mes$PG <- inver_total * as.vector(as.matrix(fin_mes[, c("raapl", "rjnj", "rko")]) %*% w)
pg <- na.omit(fin_mes$PG)
var95 <- quantile(pg, 1 - 0.95, type = 6, names = FALSE)
var99 <- quantile(pg, 1 - 0.99, type = 6, names = FALSE)Tabla P2.5. VaR mensual del portafolio
tabla_var <- data.frame(
USD = c(inver_total, length(pg), var95, var99),
Pct_del_portafolio = c(NA, NA, var95 / inver_total, var99 / inver_total),
row.names = c("Inversión total (USD)", "Número de retornos mensuales",
"VaR (95%) total", "VaR (99%) total")
)
dt_tabla(tabla_var, dec = 4, caption = "VaR mensual del portafolio")Gráfico P2.2. Distribución de los retornos mensuales del portafolio (P&G) con el VaR al 95 % y al 99 %
g_var <- ggplot(data.frame(pg = pg), aes(x = pg)) +
geom_histogram(bins = 30, fill = AZUL, alpha = 0.75, color = "white") +
geom_vline(xintercept = var95, color = NARANJA, linetype = "dashed", linewidth = 1) +
geom_vline(xintercept = var99, color = ROJO, linetype = "dashed", linewidth = 1) +
labs(title = "Distribución de la ganancia/pérdida mensual del portafolio",
subtitle = "Líneas: VaR 95% (naranja) y VaR 99% (rojo)",
x = "Ganancia / pérdida mensual (USD)", y = "Meses") +
theme_minimal()
ggplotly(g_var)La beta sirve para medir qué tanto se mueve una acción cuando cambia el mercado. Para calcularla en Apple, Johnson & Johnson y Coca Cola tomamos como referencia el S&P 500 ya que las tres hacen parte de este índice. Después con las betas de cada empresa, se calculó la beta del portafolio teniendo en cuenta el peso que tiene cada acción.
Se usaron los precios de cierre diarios del S&P 500 desde el 30 de agosto de 2016 hasta el 28 de agosto de 2026 tomando las mismas fechas que para las acciones. A partir de estos precios se calcularon los retornos diarios usando la misma fórmula utilizada anteriormente, obteniendo 2.512 datos, Luego comparamos el comportamiento diario de cada acción con el del S&P 500 para obtener su beta por último la beta del portafolio se calculó combinando las betas de las tres acciones según los pesos definidos anteriormente.
Apple es más sensible a los movimientos del mercado porque su beta es mayor a 1. si el S&P 500 sube o baja 1%, se espera que Apple se mueva aproximadamente 1,19% en la misma dirección. Por eso es una acción con más riesgo de mercado. Cuando el mercado sube puede subir más, pero cuando cae también puede tener una caída mayor. Su R² de 0,549 nos muestra que alrededor del 55% de los cambios en sus retornos están relacionados con los movimientos del mercado. J&J tiene un comportamiento más estable frente al mercado ya que si el S&P 500 se mueve 1%, se espera que J&J se mueva alrededor de 0,44%. Su R² de 0,175 indica que solo el 17,5% de los cambios en sus retornos se relacionan con el mercado y el resto está más relacionado con situaciones propias de la empresa, como sus resultados, medicamentos o litigios. Coca Cola también tiene un comportamiento más defensivo, su beta de 0,50 significa que normalmente se mueve cerca de la mitad de lo que se mueve el mercado. Además, su R² de 0,238 muestra que menos de una cuarta parte de sus movimientos se explican por el comportamiento del mercado.
Ahora bien, ya analizamos las betas individuales, la beta de portafolio nos dice que el portafolio es menos sensible que el mercado. Con una beta de 0,75, si el S&P 500 cae 1%, se esperaría que el portafolio caiga aproximadamente 0,75%. si el índice bajara 10% el portafolio podría caer cerca de 7,5% lo que representaría unos 1,5 millones de dólares sobre los 20 millones de dólares invertidos. Además de todo esto sabemos que Apple aporta la mayor parte de la beta, aportando 0,476, que representa cerca del 63% de la beta general. J&J aporta 0,164 y CocaCola 0,112 ayudando a reducir la beta.
La beta del portafolio también nos sirve para definir cuánto del riesgo de mercado se busca cubrir con los futuros, La beta sirve para saber qué parte del portafolio está expuesta a los movimientos del mercado. En este caso la beta es 0,752 y por eso la exposición al mercado es de unos 15,04 millones y no de los 20 millones completos. Además de esto los futuros sobre el S&P 500 ayudan a proteger esta parte del riesgo. Sin embargo, no cubren los problemas propios de cada empresa y por último, la cobertura también funciona mejor con Apple porque su comportamiento tiene una mayor relación con el S&P 500. En J&J y Coca Cola esta relación es menor y por eso sus movimientos pueden ser diferentes a los del índice.
rm_anual <- exp(mean(ret$rgspc) * dias) - 1
sd_indice <- sdp(ret$rgspc) * sqrt(dias)
indice_fin <- tail(datos$gspc, 1)
futuro_fin <- tail(datos$f_gspc, 1)
base <- futuro_fin - indice_fin
prima <- rm_anual - rf
beta_slope <- sapply(R, function(x) coef(lm(x ~ ret$rgspc))[2])
names(beta_slope) <- names(R)
capm_ret <- rf + beta_slope * prima
beta_cov <- sapply(R, function(x) covarp(x, ret$rgspc) / varp(ret$rgspc))
correl <- sapply(R, function(x) cor(x, ret$rgspc))
r2 <- correl^2
capm <- data.frame(
Beta = beta_slope, CAPM = capm_ret, Beta_cov_var = beta_cov,
Correlacion = correl, R2 = r2, Peso = w, Peso_x_Beta = w * beta_slope
)
beta_port <- sum(w * beta_slope)
capm["Beta Portafolio", ] <- c(beta_port, sum(w * capm_ret), NA, NA, NA, sum(w), sum(w * beta_slope))Tabla P2.6. Índice S&P 500 y prima de riesgo de mercado
indice <- data.frame(
Valor = c(length(ret$rgspc), rm_anual, sd_indice, indice_fin, futuro_fin, base, prima),
row.names = c("Número de retornos diarios", "Retorno anual efectivo",
"Desviación estándar anualizada", "Índice el 28/08/2026",
"Futuro el 28/08/2026", "Base (futuro - índice)", "Prima de riesgo de mercado")
)
dt_tabla(indice, dec = 6, caption = "S&P 500")Tabla P2.7. Betas, rendimiento CAPM y beta del portafolio
Gráfico P2.3. Beta de cada acción (línea de regresión del retorno diario contra el S&P 500)
df_reg <- data.frame(rgspc = ret$rgspc, aapl = R$aapl, jnj = R$jnj, ko = R$ko) %>%
pivot_longer(c(aapl, jnj, ko), names_to = "accion", values_to = "retorno") %>%
mutate(accion = toupper(accion))
g_beta <- ggplot(df_reg, aes(x = rgspc, y = retorno, color = accion)) +
geom_point(alpha = 0.25, size = 0.8) +
geom_smooth(method = "lm", se = FALSE, linewidth = 1.1) +
facet_wrap(~accion, scales = "free_y") +
scale_color_manual(values = c(AAPL = AZUL, JNJ = VERDE, KO = NARANJA)) +
labs(title = "Retorno diario de cada acción vs. retorno diario del S&P 500",
subtitle = "La pendiente de cada línea es la beta de la acción",
x = "Retorno diario del S&P 500", y = "Retorno diario de la acción", color = NULL) +
theme_minimal()
ggplotly(g_beta)Para cubrir el riesgo del portafolio se deben vender futuros sobre el S & P 500, como una beta de 0,752 y un portafolio de 20 millones de dólares se necesitan aproximadamente 39 contratos.
El cálculo inicial nos da 38,95 contratos, sin embargo, como no se pueden comprar o vender fracciones de contratos se debe escoger entre 38 y 39, se eligieron 39 contratos porque permiten cubrir casi todo el riesgo del mercado. Con esta cantidad se cubren cerca de 15,06 millones de dólares y queda una pequeña parte por encima de lo necesario.
La posición es corta porque el portafolio ya tiene las acciones y si el mercado cae las acciones pierden valor, pero los futuros generan una ganancia que ayuda a compensar esa pérdida. Si el mercado sube ocurre lo contrario: las acciones ganan y los futuros generan una pérdida.
Hay que tener en cuenta que esta cobertura no elimina todos los riesgos ya que los futuros protegen principalmente frente a las caídas generales del mercado, pero no frente a problemas propios de Apple J&J o Coca-Cola. Además, la beta puede cambiar con el tiempo y el precio de los futuros también cambia, por esto es que el número de contratos tendría que revisarse cuando se haga una nueva cobertura.
multiplicador <- 50
expo_contrato <- futuro_fin * multiplicador
monto_cubrir <- beta_port * inver_total
N_opt <- beta_port * (inver_total / (futuro_fin * multiplicador))
N_redondeado <- round(N_opt, 0)
contratos <- c(floor(N_opt), ceiling(N_opt))
distancia <- contratos - N_opt
expo_cub <- contratos * expo_contrato
dif_usd <- expo_cub - monto_cubrir
dif_pct <- dif_usd / monto_cubrir
beta_cub <- expo_cub / inver_total
beta_resid <- beta_port - beta_cub
opcion <- ifelse(abs(distancia[2]) <= abs(distancia[1]), "Hacia arriba", "Hacia abajo")Tabla P2.8. Cálculo del número óptimo de contratos
calculo <- data.frame(
Valor = c(miles(expo_contrato), miles(monto_cubrir, 2), round(N_opt, 6), N_redondeado, "Corta (venta de futuros)"),
row.names = c("Exposición por contrato (USD)", "Monto a cubrir (USD)",
"N contratos óptimos (sin redondear)", "N contratos (redondeado)", "Posición")
)
dt_tabla(calculo, caption = "Número óptimo de contratos")Tabla P2.9. Justificación del redondeo
redondeo <- data.frame(
Redondeo_abajo = c(contratos[1], distancia[1], expo_cub[1], dif_usd[1], dif_pct[1], beta_cub[1], beta_resid[1]),
Redondeo_arriba = c(contratos[2], distancia[2], expo_cub[2], dif_usd[2], dif_pct[2], beta_cub[2], beta_resid[2]),
row.names = c("Número de contratos", "Distancia al óptimo", "Exposición cubierta (USD)",
"Diferencia vs. monto a cubrir (USD)", "Diferencia %", "Beta cubierta", "Beta residual")
)
dt_tabla(redondeo, dec = 6, caption = "Redondeo")## Opción elegida: Hacia arriba - 39 contratos
El administrador ya posee el portafolio accionario y quiere protegerse frente a una caída del mercado antes de vender o de que venza su horizonte de inversión. Ese es exactamente el caso de una cobertura en corto. El objetivo no es eliminar el riesgo por completo, sino reducirlo; cubrir implica también renunciar a parte de la ganancia si el mercado sube, no solo evitar la pérdida si baja.
Hay dos posiciones posibles en futuros según el objetivo de cobertura, un corto, que se usa cuando el inversionista ya posee el activo y teme una caída de precio antes de vender y cubre el riesgo de que el valor de las acciones baje; y un largo, que se usa cuando el inversionista planea comprar el activo o reingresar al mercado más adelante y teme que el precio suba antes de esa fecha, cubriendo el riesgo de que el costo de entrada suba.
La cobertura con futuros sobre índice cubre el riesgo sistemático de la cartera, no el riesgo específico de cada acción, que ya se diversifica al construir el portafolio con varios títulos.
Si el mercado accionario sube, el portafolio de acciones gana valor, pero la posición corta en futuros pierde un monto similar; el resultado neto es aproximadamente el costo de oportunidad de no haber capturado toda la subida, aunque la cartera no pierde dinero en términos absolutos.
Si el mercado accionario baja, el portafolio de acciones pierde valor, pero la posición corta en futuros gana un monto similar; esa ganancia compensa la pérdida en las acciones y estabiliza el valor total de la cartera cerca del nivel que tenía al abrir la cobertura.
En ambos casos, acciones más futuros terminan moviéndose más con la tasa libre de riesgo y el beta de la cartera, no uno a uno con el índice. Un ejemplo lo deja claro, con una cartera de USD 5,050,000, B = 1.5, futuro del S&P500 en 1,010, 1 un punto equivale a USD 250, si el índice cae 10% (futuro a 909), los 30 contratos cortos ganan USD 757,500, y eso deja la cartera prácticamente en lo que el CAPM esperaba, en vez de absorber toda la caída.
beta_p <- beta_port
V_A <- inver_total
precio_futuro_indice <- futuro_fin
V_F <- precio_futuro_indice * multiplicador
N_estrella <- beta_p * (V_A / V_F)
N_contratos <- round(N_estrella)
# Verificación con el ejemplo de clase (Hull sección 3.5)
beta_ej <- 1.5; V_A_ej <- 5050000; precio_futuro_ej <- 1010; multiplicador_ej <- 250
V_F_ej <- precio_futuro_ej * multiplicador_ej
N_ej <- beta_ej * (V_A_ej / V_F_ej)
N_ej_redondeado <- round(N_ej)
caida_indice <- -0.10
precio_futuro_nuevo <- precio_futuro_ej * (1 + caida_indice)
ganancia_total_corta <- (precio_futuro_ej - precio_futuro_nuevo) * multiplicador_ej * N_ej_redondeado
perdida_cartera <- V_A_ej * beta_ej * caida_indice
cat(sprintf("Verificación con el ejemplo de clase: N* = %.2f -> %d contratos\n", N_ej, N_ej_redondeado))## Verificación con el ejemplo de clase: N* = 30.00 -> 30 contratos
cat(sprintf("Si el índice cae 10%%: ganancia en futuros %s, pérdida esperada en acciones %s, resultado neto %s\n",
miles(ganancia_total_corta,2), miles(perdida_cartera,2), miles(perdida_cartera + ganancia_total_corta, 2)))## Si el índice cae 10%: ganancia en futuros 757.500, pérdida esperada en acciones -757.500, resultado neto 0
Tabla P2.10. Sensibilidad de la cartera real cubierta ante movimientos del índice
escenarios_indice <- c(-0.10, -0.05, 0.0, 0.05, 0.10)
futuro_nuevo <- precio_futuro_indice * (1 + escenarios_indice)
ganancia_futuros <- (precio_futuro_indice - futuro_nuevo) * multiplicador * N_contratos
cambio_cartera_acciones <- V_A * beta_p * escenarios_indice
resultado_neto <- cambio_cartera_acciones + ganancia_futuros
tabla_p7 <- data.frame(
Variacion_indice = sprintf("%+.0f%%", escenarios_indice * 100),
Cambio_acciones = round(cambio_cartera_acciones, 0),
GP_futuros = round(ganancia_futuros, 0),
Resultado_neto = round(resultado_neto, 0)
)
dt_tabla(tabla_p7, caption = "Sensibilidad de la cartera cubierta")Gráfico P2.4. Resultado neto de la cartera cubierta ante escenarios del índice
df_p7 <- tabla_p7 %>%
pivot_longer(c(Cambio_acciones, GP_futuros, Resultado_neto), names_to = "serie", values_to = "valor")
g_p7 <- ggplot(df_p7, aes(x = Variacion_indice, y = valor, fill = serie)) +
geom_col(position = position_dodge(0.7), width = 0.6) +
scale_fill_manual(values = c(Cambio_acciones = GRIS, GP_futuros = NARANJA, Resultado_neto = AZUL),
labels = c("Cambio en acciones", "G/P en futuros", "Resultado neto")) +
labs(title = "Sensibilidad de la cartera cubierta (39 contratos cortos)",
x = "Variación del índice", y = "USD", fill = NULL) +
theme_minimal()
ggplotly(g_p7)Con los 39 contratos cortos del punto 6 evaluamos el primer trimestre de cobertura con liquidaciones mensuales.
Para el margen usamos el dato real de CME, el margen de mantenimiento de USD 23,164 por contrato. CME no publica directamente el margen inicial, lo fija el broker como un recargo sobre el de mantenimiento, así que mantenemos el supuesto propio de una relación de 1.10 entre ambos, margen inicial de USD 25,480.40 por contrato y margen de mantenimiento de USD 23,164 por contrato, es decir USD 993,735.60 y USD 903,396.00 para los 39 contratos.
Tabla 16. Flujos de la cuenta de margen en el primer trimestre
| Mes | Precio del futuro | Variación (Δ puntos) | G/P posición larga | G/P posición corta (39 contratos) | Saldo cuenta de margen antes del llamado |
|---|---|---|---|---|---|
| 0 (apertura) | 7,722.00 | — | — | — | 993,735.60 |
| 1 | 7,850.00 | +128.00 | +249,600.00 | −249,600.00 | 744,135.60 |
| 2 | 7,690.00 | −160.00 | −312,000.00 | +312,000.00 | 1,305,735.60 |
| 3 | 7,605.00 | −85.00 | −165,750.00 | +165,750.00 | 1,471,485.60 |
G/P se calcula como cambio en puntos × 50 USD × 39 contratos, con signo contrario entre la posición larga y la corta.
Tabla 17. Llamados al margen
| Mes | Saldo antes del llamado | Margen de mantenimiento total | ¿Llamado al margen? | Reposición para volver al margen inicial |
|---|---|---|---|---|
| 1 | 744,135.60 | 903,396.00 | Sí (saldo < mantenimiento) | 249,600.00 |
| 2 | 1,305,735.60 | 903,396.00 | No (saldo > mantenimiento) | — |
| 3 | 1,471,485.60 | 903,396.00 | No | — |
En el mes 1 el índice sube y la posición corta pierde USD 249,600; como el saldo queda por debajo del margen de mantenimiento, se dispara un llamado al margen y toca reponer esos mismos USD 249,600 para volver al margen inicial de USD 993,735.60. En los meses 2 y 3 el índice baja, la posición corta gana y el saldo se va acumulando muy por encima del mantenimiento, así que no hay más llamados.
Cuando el mercado sube la posición corta pierde, pero eso lo compensa la ganancia en las acciones; cuando baja la posición corta gana y compensa la pérdida en acciones. Eso sí, la volatilidad del S&P500 trae consigo el riesgo de liquidez del llamado al margen, la empresa necesita tener el efectivo a mano para reponer, así la cobertura esté funcionando bien en magnitud.
N_contratos <- 39
precio_apertura <- futuro_fin
margen_mantenimiento_contrato <- 23164
margen_inicial_contrato <- margen_mantenimiento_contrato * 1.10
margen_inicial_total <- margen_inicial_contrato * N_contratos
margen_mantenimiento_total <- margen_mantenimiento_contrato * N_contratos
precios_mensuales <- c(7850.00, 7690.00, 7605.00)
meses <- 1:3
filas <- list()
precio_anterior <- precio_apertura
saldo <- margen_inicial_total
for (i in seq_along(meses)) {
precio <- precios_mensuales[i]
delta_puntos <- precio - precio_anterior
gp_posicion_larga <- delta_puntos * multiplicador * N_contratos
gp_posicion_corta <- -gp_posicion_larga
saldo_antes_llamado <- saldo + gp_posicion_corta
llamado <- saldo_antes_llamado < margen_mantenimiento_total
if (llamado) {
reposicion <- margen_inicial_total - saldo_antes_llamado
saldo_despues <- margen_inicial_total
} else {
reposicion <- 0
saldo_despues <- saldo_antes_llamado
}
filas[[i]] <- data.frame(mes = meses[i], precio_futuro = precio, delta_puntos = delta_puntos,
gp_posicion_corta = gp_posicion_corta, saldo_antes_llamado = saldo_antes_llamado,
margen_mantenimiento_total = margen_mantenimiento_total,
llamado_margen = llamado, reposicion = reposicion,
saldo_despues_llamado = saldo_despues)
saldo <- saldo_despues
precio_anterior <- precio
}
df_margen <- do.call(rbind, filas)Tabla P2.11. Flujos de la cuenta de margen en el primer trimestre (posición corta, 39 contratos)
cat(sprintf("Margen inicial total: %s | Margen de mantenimiento total: %s\n",
miles(margen_inicial_total, 2), miles(margen_mantenimiento_total, 2)))## Margen inicial total: 993.735,6 | Margen de mantenimiento total: 903.396
Gráfico P2.5. Evolución del saldo de la cuenta de margen en el primer trimestre
df_saldo <- rbind(
data.frame(mes = 0, saldo = margen_inicial_total, tipo = "Después del llamado"),
data.frame(mes = df_margen$mes, saldo = df_margen$saldo_antes_llamado, tipo = "Antes del llamado"),
data.frame(mes = df_margen$mes, saldo = df_margen$saldo_despues_llamado, tipo = "Después del llamado")
) %>% arrange(mes, tipo)
g_margen <- ggplot(df_saldo, aes(x = mes, y = saldo)) +
geom_hline(yintercept = margen_mantenimiento_total, color = ROJO, linetype = "dashed") +
geom_hline(yintercept = margen_inicial_total, color = VERDE, linetype = "dotted") +
geom_line(color = AZUL, linewidth = 1) +
geom_point(aes(color = tipo), size = 2.5) +
scale_color_manual(values = c("Antes del llamado" = NARANJA, "Después del llamado" = AZUL)) +
scale_x_continuous(breaks = 0:3, labels = c("Apertura", "Mes 1", "Mes 2", "Mes 3")) +
labs(title = "Saldo de la cuenta de margen vs. margen de mantenimiento (línea roja) e inicial (línea verde)",
x = NULL, y = "USD", color = NULL) +
theme_minimal()
ggplotly(g_margen)Como el horizonte es de cuatro años y los futuros sobre índices vencen cada trimestre, la cobertura corta del punto 7 no se queda en un solo contrato, al cerrar cada trimestre, se liquida el contrato que está por vencer y se abre uno equivalente en el siguiente disponible. Esto es un roll over.
Tomemos el cierre del primer trimestre, se cierra la posición corta comprando los 39 contratos al precio actual y se abre una nueva posición corta vendiendo 39 contratos en el contrato del siguiente trimestre, a su propio precio inicial, que no tiene por qué coincidir con el precio de cierre del anterior, porque son instrumentos distintos, cada uno con su propia base.
Tabla 18. Roll-over al cierre del primer trimestre
| Concepto | Cálculo | Resultado |
|---|---|---|
| G/P realizada al cerrar Q1 (corto) | −(7,605 – 7,722) × 50 × 39 | +228,150 USD |
| Base al cierre de Q1 (spot − futuro) | 7,595 – 7,605 | −10 pts |
| Apertura del contrato de Q2 (nuevo F₀) | Dato de mercado al momento del roll over | 7,620 pts |
| Base al abrir Q2 (spot − futuro nuevo) | 7,595 – 7,620 | −25 pts |
| Diferencia de roll (F₀ nuevo − F cierre anterior) | 7,620 – 7,605 | +15 pts → costo de 29,250 USD para el corto |
La diferencia de roll over aparece porque el nuevo contrato casi nunca abre exactamente donde cerró el anterior, si abre más caro renovar el corto cuesta, ya que terminas vendiendo relativamente barato comparado con lo que valdría ese contrato si la base se comportara igual que antes. Si abriera más barato, el roll over sería favorable para el corto. Este costo o beneficio se va sumando trimestre a trimestre.
Con 16 roll overs en todo el horizonte, el resultado final ya no depende solo de para dónde se movió el índice cada trimestre, sino también de si esas renovaciones fueron en promedio favorables o costosas para el corto, cuando el futuro cotiza sistemáticamente por encima del spot renovar tiende a costar; si cotizara por debajo, sería al revés. Además, los 39 contratos óptimos del punto 6 no son fijos para siempre, hay que revisarlos en cada roll over porque el valor de la cartera y su beta van cambiando.
Ya existe dentro de un mismo trimestre, pero el roll over lo multiplica, en vez de un solo punto de incertidumbre al final, hay uno en cada renovación. Cada vez que se cierra y se abre, el precio que realmente quedó asegurado es F0 + base de cierre, y esa base cambia cada trimestre. Por eso la cobertura con roll over nunca fija un precio único para todo el horizonte, como sí lo haría un forward a cuatro años.
Mantenerse siempre corto es el diseño de la cobertura, protege al que ya tiene las acciones y se mantiene mientras siga en largo en acciones. Estar en largo en algún momento no sería cobertura sino especulación, duplicaría la exposición al mercado en vez de compensarla, ganando más si sube, pero perdiendo más si baja, y eso aumenta la volatilidad total en vez de reducirla.
F_apertura_Q1 <- 7722.00
F_cierre_Q1 <- 7605.00
spot_cierre_Q1 <- 7595.00
F_apertura_Q2 <- 7620.00
gp_realizada_Q1 <- -(F_cierre_Q1 - F_apertura_Q1) * multiplicador * N_contratos
base_cierre_Q1 <- spot_cierre_Q1 - F_cierre_Q1
base_apertura_Q2 <- spot_cierre_Q1 - F_apertura_Q2
diferencia_roll <- F_apertura_Q2 - F_cierre_Q1
costo_roll <- diferencia_roll * multiplicador * N_contratos
rollover <- function(F_apertura_actual, F_cierre_actual, F_apertura_siguiente, spot_cierre,
N = N_contratos, mult = multiplicador) {
gp_realizada <- -(F_cierre_actual - F_apertura_actual) * mult * N
base_cierre <- spot_cierre - F_cierre_actual
base_apertura_nueva <- spot_cierre - F_apertura_siguiente
diferencia_roll <- F_apertura_siguiente - F_cierre_actual
costo_roll <- diferencia_roll * mult * N
list(gp_realizada = gp_realizada, base_cierre = base_cierre, base_apertura_nueva = base_apertura_nueva,
diferencia_roll = diferencia_roll, costo_roll = costo_roll)
}
resultado_Q1_a_Q2 <- rollover(F_apertura_Q1, F_cierre_Q1, F_apertura_Q2, spot_cierre_Q1)Tabla P2.12. Roll-over al cierre del primer trimestre
tabla_roll <- data.frame(
Concepto = c("G/P realizada al cerrar Q1 (corto)", "Base al cierre de Q1", "Apertura del contrato Q2",
"Base al abrir Q2", "Diferencia de roll", "Costo/beneficio del roll para el corto"),
Resultado = c(gp_realizada_Q1, base_cierre_Q1, F_apertura_Q2, base_apertura_Q2, diferencia_roll, costo_roll)
)
dt_tabla(tabla_roll, dec = 2, caption = "Roll-over Q1 a Q2")Usamos la misma tasa libre de riesgo del punto 6, 4.445% anual, el promedio entre el treasury a 3 años (4.41%) y a 5 años (4.48%) al 28 de agosto de 2026. Promediar esos dos plazos nos da una tasa consistente con el horizonte de la cobertura.
La idea del CAPM es sencilla, cubrir con futuros elimina el riesgo sistemático de la cartera, y una vez desaparece, el rendimiento esperado ya no depende de la prima de riesgo de mercado, sino que converge a la tasa libre de riesgo. La cartera sin cubrir, en cambio, sigue esperando el rendimiento completo del CAPM que calculamos en el punto 6.
Tabla 19. Valor esperado trimestral con y sin cobertura
| Posición | Tasa esperada anual | Tasa esperada trimestral | Valor esperado (USD 20,000,000) |
|---|---|---|---|
| Sin cobertura | 16.23 % | 3.83 % | 20,766,321 |
| Cubierta | 4.445 % | 1.09 % | 20,218,639 |
La tasa trimestral se calcula como (1+r) ^(1/4) − 1, para mantener la misma convención de interés compuesto. La diferencia entre los dos valores esperados es de $547,682 al cierre del primer trimestre, o sea 2.74 puntos porcentuales aproximadamente 25% de la prima de riesgo de mercado anual que la cobertura elimina.
La cobertura reduce tanto el riesgo como el rendimiento esperado. Al vender los 39 contratos, se deja de recibir la prima de riesgo sistemático que paga el mercado por estar expuesto al índice y a cambio la cartera se comporta casi como un activo libre de riesgo sistemático, 16.23% anual sin cobertura vs. 4.445% cubierta, menos la parte de la volatilidad que venía justamente de ese beta.
La cobertura no elimina toda la volatilidad, sólo la sistemática, el riesgo propio de AAPL, JNJ y KO continúa existiendo, porque con solo tres acciones la cartera está lejos de ser diversificada. Por eso la cartera cubierta todavía tiene una desviación estándar positiva, aunque más baja, aun cuando el rendimiento esperado ya converge a la tasa libre de riesgo. Ese es el trade off de fondo de cualquier cobertura con futuros de índice, renuncias al rendimiento sistemático a cambio de estabilidad frente al mercado, pero el riesgo de haber escogido esas acciones específicas se queda.
r_CAPM_anual <- ret_esp
r_f_anual <- rf
r_CAPM_trimestral <- (1 + r_CAPM_anual)^(1 / 4) - 1
r_f_trimestral <- (1 + r_f_anual)^(1 / 4) - 1
VE_sin_cobertura_q <- inver_total * (1 + r_CAPM_trimestral)
VE_cubierta_q <- inver_total * (1 + r_f_trimestral)
diferencia_q <- VE_sin_cobertura_q - VE_cubierta_qTabla P2.13. Valor esperado trimestral con y sin cobertura
tabla_p10 <- data.frame(
Posicion = c("Sin cobertura", "Cubierta"),
Tasa_esperada_anual = c(r_CAPM_anual, r_f_anual),
Tasa_esperada_trimestral = c(r_CAPM_trimestral, r_f_trimestral),
Valor_esperado_USD = c(VE_sin_cobertura_q, VE_cubierta_q)
)
dt_tabla(tabla_p10, dec = 6, caption = "Valor esperado trimestral")## Diferencia en USD al cierre del primer trimestre: 547.550,1
Tenemos tres formas de verlo. Sin cobertura, usando el CAPM del punto 6 el rendimiento esperado mensual queda en 1.26%, es decir un valor esperado de USD 20,252,245 sobre los 20 millones iniciales. Cubierto con futuros, como el rendimiento converge a la tasa libre de riesgo del punto 10, el mensual baja a 0.36%, con un valor esperado de USD 20,072,616. Tercer enfoque, ajustado por escenarios de VaR, le restamos al valor esperado sin cobertura el VaR mensual del punto 6 como margen de pérdida potencial.
El tercer enfoque no es un rendimiento como tal, sino un ajuste al valor esperado por el riesgo de cola ya calculado en el punto 6. Restando el VaR al valor esperado se obtiene una cifra conservadora, el nivel al que caería la cartera si, además de no cumplirse el escenario esperado, también se diera la pérdida extrema definida por cada nivel de confianza.
Tabla 20. Valor esperado ajustado por VaR
| Escenario | Cálculo | Valor ajustado |
|---|---|---|
| Esperado, ajustado por VaR 95 % | 20,252,245 – 1,368,191 | 18,884,054 |
| Esperado, ajustado por VaR 99 % | 20,252,245 – 2,226,968 | 18,025,277 |
Comparando con los otros dos enfoques, esto muestra el verdadero rango de incertidumbre de la cartera sin cubrir, el valor esperado tiene una cola de pérdida que, con 99% de confianza, dejaría la cartera casi USD 2 millones por debajo de los 20 millones en un mes. La cobertura con futuros no elimina esa cola, sólo la reduce, porque los futuros solo cubren el riesgo sistemático.
AAPL, JNJ y KO pagan dividendos, pero con rendimientos muy distintos: JNJ y KO son acciones maduras de alto dividendo (3–3.1% anual cada una), mientras AAPL paga poco en comparación con su precio (0.4–0.5% anual) por estar más orientada al crecimiento. Ponderando por los pesos del punto 6 la rentabilidad por dividendos de la cartera queda en 2.02% anual, cifra aproximada porque depende del dividendo vigente en la fecha de valoración.
El rendimiento total de una acción es la ganancia de capital más el dividendo, y como el 16.23% CAPM del punto 6 ya se calculó como rendimiento total los dividendos ya están ahí y no hay que sumarlos aparte. Lo que sí cambia es la composición, cerca de 2.02 puntos del 16.23% vienen como el dividendo, no como apreciación de precio, lo que reduce la volatilidad efectiva frente a una cartera sin dividendos.
Para la cobertura, los dividendos importan de otra forma, el futuro del índice se cotiza como F = S × e((r−q) T), con “q” siendo el dividendo del índice. Si el dividendo de nuestra cartera difiere del dividendo promedio del S&P 500, la cobertura no queda bien ni siquiera en la parte sistemática, hay un riesgo de base, pequeño comparado con el de los puntos 8 y 9, que viene de esa diferencia de dividendo y no del movimiento del índice.
r_CAPM_mensual <- (1 + r_CAPM_anual)^(1 / 12) - 1
r_f_mensual <- (1 + r_f_anual)^(1 / 12) - 1
VE_sin_cobertura_m <- inver_total * (1 + r_CAPM_mensual)
VE_cubierta_m <- inver_total * (1 + r_f_mensual)
VE_ajustado_VaR95 <- VE_sin_cobertura_m - abs(var95)
VE_ajustado_VaR99 <- VE_sin_cobertura_m - abs(var99)
pesos <- c(AAPL = 0.40, JNJ = 0.3763, KO = 0.2237)
dividend_yield <- c(AAPL = 0.0045, JNJ = 0.0305, KO = 0.0305)
dividend_yield_cartera <- sum(pesos * dividend_yield[names(pesos)])Tabla P2.14. Rendimiento esperado mensual bajo tres enfoques
tabla_p11 <- data.frame(
Enfoque = c("Sin cobertura (CAPM)", "Cubierta con futuros (converge a rf)",
"Ajustado por VaR 95%", "Ajustado por VaR 99%"),
Valor_esperado_USD = c(VE_sin_cobertura_m, VE_cubierta_m, VE_ajustado_VaR95, VE_ajustado_VaR99)
)
dt_tabla(tabla_p11, dec = 2, caption = "Tres enfoques del rendimiento mensual")## Dividend yield de la cartera (anual, ponderado): 0.0201
Repetimos el cálculo del punto 6 pero ahora con dos betas hipotéticos, 0.5 y 2, y los comparamos con el beta real de la cartera
Tabla 21. Sensibilidad de la cobertura a la beta del portafolio
| Beta | N* | Exposición sistemática | Sensibilidad a 1 % del índice | Nominal cubierto |
|---|---|---|---|---|
| 0,5 (hipotético) | 25.90 → 26 cortos | 10,000,000 | 100,000 | 10,038,600 |
| 0,752 (real) | 38.95 → 39 cortos | 15,040,000 | 150,400 | 15,057,900 |
| 2,0 (hipotético) | 103.60 → 104 cortos | 40,000,000 | 400,000 | 40,154,400 |
Con B = 0.5 la cartera se mueve en promedio la mitad de lo que se mueve el índice, así que su exposición sistemática es de USD 10,000,000 y con solo 26 contratos cortos, 13 menos que con el B real. Un 1% de movimiento en el índice mueve la cartera apenas USD 100,000. Es una cobertura más suave porque hay menos riesgo sistemático que cubrir; tendría sentido para una cartera cargada a acciones defensivas o de bajo B.
Con B = 2 la cartera amplifica el movimiento del índice, su exposición sistemática es el doble del valor de la cartera, de ahí los 104 contratos cortos frente a los 39 reales. Un 1% de movimiento en el índice mueve la cartera USD 400 mil, cuatro veces más que en el caso de B = 0.5. Que el nominal cubierto sea mayor que la propia cartera es normal cuando B > 1, cubrir el riesgo sistemático pide una posición en futuros más grande que el activo mismo, porque la cartera reacciona más que uno a uno frente al mercado.
N* es proporcional a B porque te dice cuánto se mueve la cartera por cada unidad que se mueve el índice; para contrarrestar ese movimiento con una posición que también depende del índice, el tamaño de esa posición tiene que subir igual. Una cartera con B alto concentra más riesgo no diversificable y es justo ese riesgo, el que los futuros sobre índices pueden compensar. Así que duplicar o cuadruplicar B no cambia la lógica de la cobertura, solo su escala, se necesitan más contratos, el nominal cubierto crece en la misma proporción, y el costo y el riesgo de base de la posición también suben.
precio_futuro_p12 <- futuro_fin
V_F_p12 <- precio_futuro_p12 * multiplicador
betas_p12 <- c("0.5 (hipotético)" = 0.5, "0.752 (real, punto 6)" = beta_port, "2.0 (hipotético)" = 2.0)
filas_p12 <- list()
for (etiqueta in names(betas_p12)) {
b <- betas_p12[[etiqueta]]
N_est <- b * (inver_total / V_F_p12)
N_c <- round(N_est)
filas_p12[[etiqueta]] <- data.frame(
Beta = b, N_estrella = N_est, N_contratos = N_c,
Exposicion_sistematica = b * inver_total,
Sensibilidad_1pct = b * inver_total * 0.01,
Nominal_cubierto = N_c * V_F_p12
)
}
tabla_p12 <- do.call(rbind, filas_p12)Tabla P2.15. Sensibilidad de la cobertura a la beta del portafolio
Gráfico P2.6. Número de contratos y nominal cubierto según la beta del portafolio
tabla_p12$etiqueta <- rownames(tabla_p12)
g_p12 <- ggplot(tabla_p12, aes(x = etiqueta, y = N_contratos, fill = etiqueta,
text = paste0("N* = ", round(N_estrella, 2), " -> ", N_contratos, " contratos"))) +
geom_col(width = 0.6) +
scale_fill_manual(values = c(AZUL, VERDE, NARANJA)) +
labs(title = "Número de contratos cortos necesarios según la beta del portafolio",
x = NULL, y = "Contratos", fill = NULL) +
theme_minimal() + theme(legend.position = "none")
ggplotly(g_p12, tooltip = "text")El trabajo aborda dos frentes distintos del riesgo que enfrenta una empresa colombiana con exposición al dólar: una deuda real en moneda extranjera (Parte 1, forwards de divisas sobre un crédito de maquinaria) y un portafolio accionario con exposición al mercado estadounidense (Parte 2, futuros sobre el índice S&P 500). En ambos casos la conclusión de fondo es la misma, cubrir con derivados no elimina el riesgo, lo transforma y tiene un costo. En la Parte 1, la cobertura con forwards para los años 7 a 10 del crédito sube el costo esperado en pesos en 1.46 % frente a no cubrir nada, porque el forward ya trae incorporada la prima de paridad de tasas de interés; a cambio, reduce de forma marcada el riesgo de cola en escenarios de depreciación extrema del peso. En la Parte 2, cubrir la beta de 0.752 del portafolio con 39 contratos cortos de futuros del S&P 500 hace que el rendimiento esperado converja de 16.23 % a la tasa libre de riesgo de 4.445 % anual, renunciando a la prima de riesgo sistemático a cambio de estabilidad frente al mercado, sin eliminar el riesgo propio de AAPL, JNJ y KO. En los dos casos el resultado depende de supuestos explícitos (el spread sobre las tasas en la Parte 1, el margen inicial como múltiplo del de mantenimiento en la Parte 2) que quedan documentados junto con su fuente para que el lector pueda evaluar su razonabilidad y, si lo considera necesario, recalcular los resultados con otros supuestos usando el mismo código.
24/7 Wall St. (2026a, 8 de julio). Apple's $100 billion buyback machine keeps Wall Street watching.
24/7 Wall St. (2026b, 4 de septiembre). Johnson & Johnson is boring, which makes its dividend nearly perfect.
24/7 Wall St. (2026c, 14 de septiembre). Coca-Cola stock is up 29% in 2026: What will it take to break through $100? https://247wallst.com/investing/2026/09/14/coca-cola-stock-is-up-29-in-2026-what-will-it-take-to-break-through-100/
24/7 Wall St. (2026d, 25 de septiembre). Apple stock outlook: Our target leaves Wall Street behind.
Ad-hoc-news. (2026, 15 de septiembre). Coca-Cola stock gains on raised 2026 guidance and strong dividend signal. https://www.ad-hoc-news.de/boerse/news/corporate-news/coca-cola-stock-gains-on-raised-20 26-guidance-and-strong-dividend-signal/70105661
Apple Inc. (2026). Condensed consolidated financial statements: Fiscal year 2026, third quarter. https://www.apple.com/newsroom/pdfs/fy2026q3/FY26_Q3_Consolidated_Financial_Stateme nts.pdf
Johnson & Johnson. (2026, julio). Johnson & Johnson reports Q2 2026 results, raises 2026 outlook \[Comunicado de prensa\]. https://www.jnj.com/media-center/press-releases/johnson-johnson-reports-q2-2026-results-rai ses-2026-outlook
Slickcharts. (s.f.). S&P 500 companies by weight. Recuperado el 1 de octubre de 2026, de https://www.slickcharts.com/sp500
StockAnalysis. (s.f.-a). Apple (AAPL) stock price history. Recuperado el 2 de octubre de 2026, de https://stockanalysis.com/stocks/aapl/history/
StockAnalysis. (s.f.-b). Coca-Cola (KO) stock price history. Recuperado el 2 de octubre de 2026, de https://stockanalysis.com/stocks/ko/history/
StockAnalysis. (s.f.-c). Johnson & Johnson (JNJ) stock forecast & analyst price targets. Recuperado el 1 de octubre de 2026, de https://stockanalysis.com/stocks/jnj/forecast/
StockAnalysis. (s.f.-d). Johnson & Johnson (JNJ) stock price history. Recuperado el 2 de octubre de 2026, de https://stockanalysis.com/stocks/jnj/history/
The Coca-Cola Company. (2026, 28 de julio). Coca-Cola reports second quarter 2026 results and raises full year guidance \[Comunicado de prensa\]. Yahoo Finance. https://finance.yahoo.com/markets/stocks/articles/coca-cola-reports-second-quarter-10550073 8.html
TIKR. (s.f.). Johnson & Johnson stock: 2026 outlook after 50% gains. https://www.tikr.com/blog/johnson-johnson-stock-2026-outlook-after-50-gains
Yahoo Finance. (2026, 15 de julio). Apple is up 20% in 2026. What will it take for AAPL stock to hit $350?
https://finance.yahoo.com/markets/stocks/articles/apple-20-2026-aapl-stock-191629157.html