library(quantmod)
library(ggplot2)
library(knitr)
library(dplyr)
# Obtener datos históricos de la TRM (USD/COP)
getSymbols("USDCOP=X", src = "yahoo", from = "2022-01-01", to = Sys.Date())
## [1] "USDCOP=X"
trm_daily <- Cl(`USDCOP=X`)
colnames(trm_daily) <- "TRM"
# Convertir a data frame para ggplot
df_trm <- data.frame(
date = index(trm_daily),
TRM = as.numeric(trm_daily$TRM)
)
# Gráfico de la TRM
ggplot(df_trm, aes(x = date, y = TRM)) +
geom_line(color = "steelblue", linewidth = 0.8)+
labs(
title = "Histórico TRM USD/COP",
x = "Fecha",
y = "COP por USD"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
axis.title.y = element_text(margin = margin(r = 10)),
axis.title.x = element_text(margin = margin(t = 10))
)
Al mirar el gráfico histórico de la TRM desde 2022 se nota que el dólar ha sido muy volátil. Hubo un pico muy alto en 2022-2023, cuando llegó incluso por encima de los $4.800. Desde entonces ha tenido subidas y bajadas, pero parece que en los últimos meses ha bajado un poco y se mantiene en un rango más estable.
Factores internacionales que inciden en la TRM
Factores internos en Colombia
library(ggplot2)
# Crear el data frame con las expectativas
expectativas_trm <- data.frame(
Fecha = c("Sep-2025", "Dic-2025", "Sep-2026", "Dic-2026", "Sep-2027"),
Promedio = c(3968, 4056, 4053, 4061, 4090),
Minimo = c(3900, 3869, 3391, 3522, 3555),
Maximo = c(4113, 4250, 4350, 4400, 4500)
)
# Convertir Fecha en factor y crear columna de énfasis sin dplyr
expectativas_trm$Fecha <- factor(expectativas_trm$Fecha, levels = expectativas_trm$Fecha)
expectativas_trm$Enfasis <- ifelse(expectativas_trm$Fecha == "Sep-2026", "Sí", "No")
# Gráfico
ggplot(expectativas_trm, aes(x = Fecha, y = Promedio, color = Enfasis)) +
geom_point(size = 3) +
geom_errorbar(aes(ymin = Minimo, ymax = Maximo),
width = 0.2, linewidth = 0.8) +
scale_color_manual(values = c("Sí" = "firebrick", "No" = "steelblue")) +
labs(
title = "Expectativas de la TRM (USD/COP)",
x = "Fecha",
y = "COP por USD",
caption = "Fuente: Encuesta de Expectativas BanRep(2025)",
color = NULL
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
axis.title.y = element_text(margin = margin(r = 10)),
axis.title.x = element_text(margin = margin(t = 10)),
plot.caption = element_text(hjust = 0, face = "italic", size = 10),
legend.position = "none"
)
Según la Encuesta de Expectativas de Analistas Económicos del Banco de la República (2025), el mercado espera que la TRM se mantenga entre $3.391 y $4.350 para los próximos doce meses con un valor promedio de $4.053
# Calcular retornos logarítmicos diarios
retornos_log <- diff(log(trm_daily))
retornos_log <- na.omit(retornos_log)
# Data frame de retornos
retornos_df <- data.frame(
Fecha = index(retornos_log),
Retorno = as.numeric(retornos_log)
)
# Agregar columna Año-Mes
retornos_df <- retornos_df %>%
mutate(AñoMes = format(Fecha, "%Y-%m"))
# Calcular retornos mensuales (compuestos)
retornos_mes <- retornos_df %>%
group_by(AñoMes) %>%
summarise(
RetornoMensual = exp(sum(Retorno, na.rm = TRUE)) - 1
)
#TABLA 1: Resumen estadístico
promedio_mensual <- mean(retornos_mes$RetornoMensual, na.rm = TRUE)
desv_mensual <- sd(retornos_mes$RetornoMensual, na.rm = TRUE)
tabla_resumen <- data.frame(
Estadístico = c("Promedio mensual", "Desviación estándar mensual"),
Valor = c(round(promedio_mensual * 100, 3), round(desv_mensual * 100, 3))
)
kable(tabla_resumen, caption = "Resumen estadístico de retornos mensuales TRM (%)")
| Estadístico | Valor |
|---|---|
| Promedio mensual | -0.028 |
| Desviación estándar mensual | 3.458 |
#GRÁFICO: Retornos mensuales
ggplot(retornos_mes, aes(x = AñoMes, y = RetornoMensual * 100)) +
geom_col(fill = "steelblue") +
geom_hline(yintercept = 0, color = "red", linetype = "dashed") +
labs(
title = "Retornos mensuales TRM (USD/COP)",
x = "Periodo",
y = "Retorno mensual (%)"
) +
theme_minimal(base_size = 13) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 8, margin = margin(t = 5)),
plot.title = element_text(face = "bold", hjust = 0.5)
)
options(scipen = 999)
set.seed(1234)
# Parámetros iniciales desde el punto 2
s0 <- as.numeric(tail(trm_daily, 1)) # Último valor de la TRM
mu <- mean(retornos_mes$RetornoMensual, na.rm = TRUE)
sigma <- sd(retornos_mes$RetornoMensual, na.rm = TRUE)
# Parámetros de simulación
N <- 5000 # Número de trayectorias
T <- 12 # Horizonte: 12 meses
mb <- matrix(ncol = N, nrow = T)
mb[1, ] <- rep(s0, N)
# Simulación MBG mensual
for (i in 1:N) {
for (t in 2:T) {
mb[t, i] <- mb[t - 1, i] * exp((mu - 0.5 * sigma^2) + sigma * rnorm(1))
}
}
#Gráfico de trayectorias simuladas
matplot(
mb,
type = "l",
lty = 1,
main = "Simulación MBG mensual TRM (USD/COP)",
xlab = "Mes",
ylab = "TRM simulada",
lwd = 1
)
# Trayectoria promedio en rojo
lines(rowMeans(mb), col = "red", lwd = 2)
# Cálculo de percentiles (intervalo de confianza)
q1 <- quantile(mb[T, ], 0.975)
q2 <- quantile(mb[T, ], 0.025)
#Distribución final
plot(
density(mb[T, ]),
main = "Distribución simulada de TRM al mes 12",
xlab = "TRM simulada",
lwd = 2
)
abline(v = q1, col = "blue", lwd = 2)
abline(v = q2, col = "green", lwd = 2)
# Supuestos
trm_spot <- as.numeric(last(trm_daily))
monto_cop <- 500000000
inicial_pct <- 0.10
precio_usd <- monto_cop / trm_spot
# Condiciones del crédito
tasa_anual <- 0.06 #TASA NOMINAL MES VENCIDO
tasa_mensual <- tasa_anual / 12 #TASA MENSUAL
n_periodos <- 10 * 12 # 10 años, pagos mensuales
# Monto financiado en USD
loan_usd <- precio_usd * (1 - inicial_pct)
# Fórmula de cuota fija (francés)
pago_mensual_usd <- (tasa_mensual * loan_usd) / (1 - (1 + tasa_mensual)^(-n_periodos))
# Creación de tabla de amortización simplificada
amort <- data.frame(
periodo = 1:n_periodos,
saldo = numeric(n_periodos),
interes = numeric(n_periodos),
principal = numeric(n_periodos),
pago = rep(pago_mensual_usd, n_periodos)
)
saldo <- loan_usd
for (t in 1:n_periodos) {
interes_t <- saldo * tasa_mensual
principal_t <- pago_mensual_usd - interes_t
saldo <- saldo - principal_t
amort$saldo[t] <- max(saldo, 0)
amort$interes[t] <- interes_t
amort$principal[t] <- principal_t
}
# Mostrar los primeros 10 periodos de la amortización
head(amort, 10) %>%
knitr::kable(digits = 2, caption = "Amortización tipo francés — primeros 10 pagos")
| periodo | saldo | interes | principal | pago |
|---|---|---|---|---|
| 1 | 115256.8 | 579.82 | 707.62 | 1287.44 |
| 2 | 114545.7 | 576.28 | 711.16 | 1287.44 |
| 3 | 113830.9 | 572.73 | 714.71 | 1287.44 |
| 4 | 113112.6 | 569.15 | 718.29 | 1287.44 |
| 5 | 112390.8 | 565.56 | 721.88 | 1287.44 |
| 6 | 111665.3 | 561.95 | 725.49 | 1287.44 |
| 7 | 110936.2 | 558.33 | 729.12 | 1287.44 |
| 8 | 110203.4 | 554.68 | 732.76 | 1287.44 |
| 9 | 109467.0 | 551.02 | 736.43 | 1287.44 |
| 10 | 108726.9 | 547.33 | 740.11 | 1287.44 |
# Mostrar resumen total en tabla
total_pagado <- sum(amort$pago)
total_interes <- sum(amort$interes)
resumen_credito <- data.frame(
Concepto = c("Monto financiado (USD)",
"Pago mensual (USD)",
"Total pagado en 10 años (USD)",
"Interés total pagado (USD)"),
Valor = c(round(loan_usd, 2),
round(pago_mensual_usd, 2),
round(total_pagado, 2),
round(total_interes, 2))
)
knitr::kable(resumen_credito,
digits = 2,
caption = "Resumen del crédito en dólares — Sistema francés")
| Concepto | Valor |
|---|---|
| Monto financiado (USD) | 115964.44 |
| Pago mensual (USD) | 1287.44 |
| Total pagado en 10 años (USD) | 154493.16 |
| Interés total pagado (USD) | 38528.72 |
# Crear tabla de pagos en USD
pagos_usd <- amort$pago
# Convertir cada pago mensual a COP según cada trayectoria simulada
pagos_cop_sim <- matrix(nrow = nrow(amort), ncol = ncol(mb))
for (i in 1:ncol(mb)) {
pagos_cop_sim[, i] <- pagos_usd * mb[, i] # usa cada TRM simulada mes a mes
}
# Calcular métricas de interés
total_cop_sim <- colSums(pagos_cop_sim) # total pagado en COP por simulación
# Resumen de percentiles y media
resumen_cop <- data.frame(
Escenario = c("Pesimista (p95)", "Promedio", "Optimista (p5)"),
TotalPagadoCOP = c(
quantile(total_cop_sim, 0.95),
mean(total_cop_sim),
quantile(total_cop_sim, 0.05)
)
)
knitr::kable(resumen_cop, digits = 0,
caption = "Total pagado en COP bajo diferentes escenarios de TRM")
| Escenario | TotalPagadoCOP | |
|---|---|---|
| 95% | Pesimista (p95) | 665630787 |
| Promedio | 598809670 | |
| 5% | Optimista (p5) | 537445684 |
# Histograma de los pagos totales en COP (valores completos)
hist(total_cop_sim,
breaks = 50,
col = "skyblue",
border = "white",
main = "Distribución de pagos totales en COP",
xlab = "Total pagado (COP)",
ylab = "Frecuencia")
# Añadir líneas verticales para media y percentiles
media <- mean(total_cop_sim)
p5 <- quantile(total_cop_sim, 0.05)
p95 <- quantile(total_cop_sim, 0.95)
abline(v = media, col = "red", lwd = 2, lty = 2)
abline(v = p5, col = "green", lwd = 2, lty = 2)
abline(v = p95, col = "orange", lwd = 2, lty = 2)
legend("topright",
legend = c("Media", "Optimista (p5)", "Pesimista (p95)"),
col = c("red", "green", "orange"), lwd = 2, lty = 2)
El histograma muestra la distribución de los pagos totales del crédito en pesos colombianos bajo diferentes trayectorias simuladas de la TRM. La línea roja punteada indica el pago promedio, alrededor de 598.952.311 COP, que representa el valor esperado del total a pagar considerando la volatilidad histórica de la TRM. La línea verde (p5) marca un escenario optimista, en el que los pagos totales podrían ser tan bajos como 537.641.880 COP, mientras que la línea naranja (p95) representa un escenario pesimista, donde los pagos podrían ascender hasta 665.713.200 COP. Esto evidencia que, aunque el pago promedio es cercano a 599 millones COP, existe un rango de incertidumbre significativo, reflejando la sensibilidad del crédito frente a la variación del tipo de cambio.