knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, fig.align = ‘center’, fig.width = 10, fig.height = 6, out.width = ‘80%’)
install.packages(“gridExtra”) install.packages(“DT”)
library(ggplot2) library(dplyr) library(tidyr) library(knitr) library(kableExtra) library(gridExtra) library(plotly) library(DT)
theme_set(theme_minimal(base_size = 12))
{r} # ============================================ #
PARÁMETROS DEL CRÉDITO # ============================================ #
Valor total de la maquinaria en COP valor_maquinaria_cop <-
350000000
trm_inicial <- 3920
valor_credito_usd <- valor_maquinaria_cop / trm_inicial
inicial_usd <- valor_credito_usd * 0.10 monto_prestamo_usd <- valor_credito_usd - inicial_usd
tasa_interes_anual <- 0.05577 ```
plazo_anos <- 10 cuotas_trimestrales <- plazo_anos * 4
tasa_trimestral <- tasa_interes_anual / 4
cuota_usd <- monto_prestamo_usd * (tasa_trimestral * (1 + tasa_trimestral)^cuotas_trimestrales) / ((1 + tasa_trimestral)^cuotas_trimestrales - 1)
formato_moneda <- function(x) { paste0(“$”, format(round(x), big.mark = “.”, scientific = FALSE)) }
tabla_parametros <- data.frame( Parametro = c( “Valor Maquinaria (COP)”, “TRM Inicial (COP/USD)”, “Valor Credito (USD)”, “Inicial 10% (USD)”, “Monto Prestamo (USD)”, “Tasa Interes Anual”, “Plazo (anos)”, “Sistema de Amortizacion”, “Frecuencia de Pago”, “Cuota Trimestral (USD)” ), Valor = c( formato_moneda(valor_maquinaria_cop), formato_moneda(trm_inicial), formato_moneda(valor_credito_usd), formato_moneda(inicial_usd), formato_moneda(monto_prestamo_usd), paste0(round(tasa_interes_anual * 100, 2), “%”), paste0(plazo_anos, ” anos (40 cuotas)“),”Frances (Cuota Fija)“,”Trimestral”, formato_moneda(cuota_usd) ), stringsAsFactors = FALSE )
print(tabla_parametros) # 3. Visualización elegante con gridExtra (ya que la tienes instalada) library(gridExtra) grid.table(tabla_parametros, rows = NULL)
kable(tabla_parametros, caption = “Tabla 1: Parametros del Credito”, format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE)
#3. Análisis Fundamental de la TRM y Expectativas
contexto_data <- data.frame( Variable = c(“Tasa Fed (EE.UU.)”, “Expectativa Recorte Fed 2026”, “Tasa Banco Republica”, “Expectativa BanRep 2026”, “Spread Tasas (COP - USD)”, “Tendencia Esperada TRM”), Valor = c(“3.50% - 3.75%”, “25 puntos base”, “9.25%”, “7.00% (proyeccion)”, “3.67%”, “Presión al alza”),
Implicacion = c(“Fortalece al dolar globalmente”, “Reduccion gradual limitada”, “Diferencial atractivo para carry trade”, “Aún alto en terminos historicos”, “Beneficia al dolar vs peso”, “Depreciacion esperada del peso”) )
Federal Reserve Board. Economía Colombiana 2026. BBVA Research
contexto_data <- contexto_data %>% mutate(across(where(is.character), ~ iconv(., from = ““, to =”UTF-8”, sub = “?”)))
kable(contexto_data, caption = “Tabla 2: Contexto Macroeconomico y Expectativas”, format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE)
meses <- 0:12 trm_proyectada_base <- trm_inicial * (1 + 0.0025)^meses # Tendencia alcista del 0.25% mensual trm_proyectada_optimista <- trm_inicial * (1 + 0.001)^meses trm_proyectada_pesimista <- trm_inicial * (1 + 0.004)^meses
df_proyeccion <- data.frame( Mes = meses, Base = trm_proyectada_base, Optimista = trm_proyectada_optimista, Pesimista = trm_proyectada_pesimista )
ggplot(df_proyeccion, aes(x = Mes)) + geom_line(aes(y = Base, color = “Escenario Base”), size = 1.2) + geom_line(aes(y = Optimista, color = “Escenario Optimista”), size = 1, linetype = “dashed”) + geom_line(aes(y = Pesimista, color = “Escenario Pesimista”), size = 1, linetype = “dashed”) + geom_ribbon(aes(ymin = Optimista, ymax = Pesimista), alpha = 0.2, fill = “gray70”) + scale_color_manual(values = c(“Escenario Base” = “blue”, “Escenario Optimista” = “green”, “Escenario Pesimista” = “red”)) + labs(title = “Proyeccion de la TRM para los Próximos 12 Meses”, x = “Mes”, y = “TRM (COP/USD)”, color = “Escenario”) + theme_minimal() + theme(legend.position = “bottom”)
set.seed(2026)
n_historico <- 60 # 60 meses (5 años) tendencia_mensual <- 0.0025 volatilidad_historica <- 0.035
retornos_historicos <- tendencia_mensual + volatilidad_historica * rt(n_historico, df = 4)
trm_historica <- numeric(n_historico + 1) trm_historica[1] <- trm_inicial
for (i in 1:n_historico) { trm_historica[i + 1] <- trm_historica[i] * exp(retornos_historicos[i]) }
media_historica <- mean(retornos_historicos) sd_historica <- sd(retornos_historicos)
df_historica <- data.frame( Mes = 0:n_historico, TRM = trm_historica )
p_historica <- ggplot(df_historica, aes(x = Mes, y = TRM)) + geom_line(color = “steelblue”, size = 1) + geom_smooth(method = “lm”, se = TRUE, color = “red”, linetype = “dashed”) + labs(title = “Serie Historica de la TRM (Ultimos 5 Anos)”, subtitle = paste(“Retorno Promedio Mensual:”, round(media_historica * 100, 2), “% | Volatilidad Mensual:”, round(sd_historica * 100, 2), “%”), x = “Mes”, y = “TRM (COP/USD)”) + theme_minimal()
print(p_historica)
n_simulaciones <- 10000 n_meses <- 12 grados_libertad <- 4
simulaciones_trm <- matrix(0, nrow = n_simulaciones, ncol = n_meses + 1) simulaciones_trm[, 1] <- trm_historica[n_historico + 1]
set.seed(2026) for (i in 1:n_simulaciones) { for (t in 1:n_meses) { retorno_mensual <- media_historica + sd_historica * rt(1, df = grados_libertad) simulaciones_trm[i, t + 1] <- simulaciones_trm[i, t] * exp(retorno_mensual) } }
trm_mes_12 <- simulaciones_trm[, n_meses + 1]
percentiles <- c(1, 5, 10, 25, 50, 75, 90, 95, 99) percentiles_valores <- quantile(trm_mes_12, probs = percentiles / 100)
estadisticas_sim <- data.frame( Estadistica = c(“TRM Actual”, “Promedio Simulado”, “Mediana (P50)”, “Desviacion Estandar”, “Percentil 5%”, “Percentil 10%”, “Percentil 25%”, “Percentil 75%”, “Percentil 95%”, “Percentil 99%”), Valor_COP_USD = c(round(simulaciones_trm[1, 1], 2), round(mean(trm_mes_12), 2), round(percentiles_valores[“50%”], 2), round(sd(trm_mes_12), 2), round(percentiles_valores[“5%”], 2), round(percentiles_valores[“10%”], 2), round(percentiles_valores[“25%”], 2), round(percentiles_valores[“75%”], 2), round(percentiles_valores[“95%”], 2), round(percentiles_valores[“99%”], 2)) )
kable(estadisticas_sim, caption = “Tabla 3: Estadisticas de las Simulaciones de TRM a 12 Meses”, format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE)
df_densidad <- data.frame(TRM = trm_mes_12)
ggplot(df_densidad, aes(x = TRM)) + geom_density(fill = “steelblue”, alpha = 0.6, color = “darkblue”, size = 1) + geom_vline(xintercept = percentiles_valores[“50%”], color = “green”, linetype = “dashed”, size = 1) + geom_vline(xintercept = percentiles_valores[“95%”], color = “red”, linetype = “dashed”, size = 1) + geom_vline(xintercept = percentiles_valores[“99%”], color = “darkred”, linetype = “dashed”, size = 1) + annotate(“text”, x = percentiles_valores[“50%”] + 50, y = 0.0009, label = “Mediana (P50)”, color = “green”, size = 4) + annotate(“text”, x = percentiles_valores[“95%”] + 80, y = 0.0008, label = “Percentil 95%”, color = “red”, size = 4) + annotate(“text”, x = percentiles_valores[“99%”] + 100, y = 0.0007, label = “Percentil 99%”, color = “darkred”, size = 4) + labs(title = “Distribucion de la TRM Simulada a 12 Meses”, subtitle = paste(“Distribucion t-Student (df =”, grados_libertad, “) | 10,000 simulaciones”), x = “TRM (COP/USD)”, y = “Densidad de Probabilidad”) + theme_minimal()
escenarios <- list( “Optimista” = list(descripcion = “Escenario favorable”, trm = percentiles_valores[“10%”]), “Base” = list(descripcion = “Escenario esperado”, trm = percentiles_valores[“50%”]), “Moderado” = list(descripcion = “Escenario adverso moderado”, trm = percentiles_valores[“75%”]), “Severo” = list(descripcion = “Escenario de estres”, trm = percentiles_valores[“90%”]), “Extremo” = list(descripcion = “Escenario de crisis”, trm = percentiles_valores[“95%”]), “Catastrofico” = list(descripcion = “Evento de cola extrema”, trm = percentiles_valores[“99%”]) )
tabla_escenarios <- data.frame( Escenario = names(escenarios), Descripcion = sapply(escenarios, function(x) x\(descripcion), TRM_COP_USD = round(sapply(escenarios, function(x) x\)trm), 2), Cuota_Trimestral_USD = round(cuota_usd, 0) )
tabla_escenarios <- tabla_escenarios %>% mutate( Cuota_Trimestral_COP = paste0(“\(", format(round(cuota_usd * TRM_COP_USD, 0), big.mark = ".")), Cuota_Anual_COP = paste0("\)”, format(round(cuota_usd * TRM_COP_USD * 4, 0), big.mark = “.”)) ) %>% select(Escenario, Descripcion, TRM_COP_USD, Cuota_Trimestral_COP, Cuota_Anual_COP)
kable(tabla_escenarios, caption = “Tabla 4: Escenarios de TRM y su Impacto en Cuotas”, format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE)
resultados_impacto <- data.frame( Escenario = names(escenarios), TRM = sapply(escenarios, function(x) x$trm) )
resultados_impacto <- resultados_impacto %>% mutate( Cuota_Trimestral_COP = cuota_usd * TRM, Cuota_Anual_COP = Cuota_Trimestral_COP * 4, Impacto_Trimestral_vs_Base = Cuota_Trimestral_COP - (cuota_usd * escenarios\(Base\)trm), Impacto_Anual_vs_Base = Impacto_Trimestral_vs_Base * 4, Impacto_Porcentual = (Cuota_Trimestral_COP / (cuota_usd * escenarios\(Base\)trm) - 1) * 100 )
tabla_impacto <- resultados_impacto %>% mutate( Cuota_Trimestral_COP = paste0(“\(", format(round(Cuota_Trimestral_COP, 0), big.mark = ".")), Cuota_Anual_COP = paste0("\)”, format(round(Cuota_Anual_COP, 0), big.mark = “.”)), Impacto_Trimestral_vs_Base = paste0(“\(", format(round(Impacto_Trimestral_vs_Base, 0), big.mark = ".")), Impacto_Anual_vs_Base = paste0("\)”, format(round(Impacto_Anual_vs_Base, 0), big.mark = “.”)), Impacto_Porcentual = paste0(round(Impacto_Porcentual, 2), “%”) ) %>% select(Escenario, Cuota_Trimestral_COP, Cuota_Anual_COP, Impacto_Trimestral_vs_Base, Impacto_Anual_vs_Base, Impacto_Porcentual)
kable(tabla_impacto, caption = “Tabla 5: Impacto en Flujo de Caja por Escenario (vs Escenario Base)”, format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE)
df_impacto_grafico <- resultados_impacto %>% mutate( Escenario = factor(Escenario, levels = names(escenarios)), Impacto_Millones = Impacto_Anual_vs_Base / 1e6 )
ggplot(df_impacto_grafico, aes(x = Escenario, y = Impacto_Millones, fill = Escenario)) + geom_bar(stat = “identity”) + geom_text(aes(label = paste0(“$”, format(round(Impacto_Millones, 0), big.mark = “.”), “M”)), vjust = -0.5, size = 3.5) + scale_fill_manual(values = c(“lightgreen”, “steelblue”, “lightblue”, “orange”, “red”, “darkred”)) + labs(title = “Impacto Anual en Flujo de Caja por Escenario”, subtitle = “Comparado con el Escenario Base (Mediana)”, x = “Escenario de TRM”, y = “Impacto Anual Adicional (Millones de COP)”) + theme_minimal() + theme(legend.position = “none”, axis.text.x = element_text(angle = 45, hjust = 1))
niveles_confianza <- c(90, 95, 99) var_resultados <- data.frame()
for (alpha in niveles_confianza) { percentil_alpha <- percentiles_valores[paste0(alpha, “%”)] cuota_var_cop <- cuota_usd * percentil_alpha cuota_base_cop <- cuota_usd * percentiles_valores[“50%”]
var_trimestral <- cuota_var_cop - cuota_base_cop var_anual <- var_trimestral * 4
var_resultados <- rbind(var_resultados, data.frame( Nivel_Confianza = paste0(alpha, “%”), VaR_Trimestral_COP = var_trimestral, VaR_Anual_COP = var_anual )) }
var_resultados_formateados <- var_resultados %>% mutate( VaR_Trimestral_COP = paste0(“\(", format(round(VaR_Trimestral_COP, 0), big.mark = ".")), VaR_Anual_COP = paste0("\)”, format(round(VaR_Anual_COP, 0), big.mark = “.”)) )
kable(var_resultados_formateados, caption = “Tabla 6: Value at Risk (VaR) Cambiario”, col.names = c(“Nivel de Confianza”, “VaR Trimestral”, “VaR Anual”), format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE) %>% add_footnote(“VaR calculado como perdida adicional vs escenario base (mediana)”)
cobertura_porcentaje <- 0.75 monto_cobertura_usd <- valor_credito_usd * cobertura_porcentaje
i_cop <- 0.0925 # Tasa Colombia i_usd <- tasa_interes_anual # Tasa EE.UU. trm_forward <- trm_inicial * (1 + i_cop) / (1 + i_usd)
beneficio_cobertura <- (percentiles_valores[“95%”] - trm_forward) * cuota_usd * 4
tabla_cobertura <- data.frame( Concepto = c(“Porcentaje de Cobertura”, “Monto Cobertura (USD)”, “Tasa Spot Inicial (COP/USD)”, “Tasa Forward 1 Ano (COP/USD)”, “Ahorro Estimado Escenario Extremo (Anual)”), Valor = c(paste0(cobertura_porcentaje * 100, “%”), paste0(“\(", format(round(monto_cobertura_usd), big.mark = ".")), paste0("\)”, format(trm_inicial, big.mark = “.”)), paste0(“\(", format(round(trm_forward, 2), big.mark = ".")), paste0("\)”, format(round(beneficio_cobertura, 0), big.mark = “.”))) )
kable(tabla_cobertura, caption = “Tabla 7: Estructura de la Cobertura con Forwards”, format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE)
escenarios_trm <- c(percentiles_valores[“50%”], percentiles_valores[“90%”], percentiles_valores[“95%”], percentiles_valores[“99%”])
nombres_escenarios <- c(“Base (P50)”, “Severo (P90)”, “Extremo (P95)”, “Catastrófico (P99)”)
cuota_sin_cobertura <- cuota_usd * escenarios_trm * 4 cuota_con_cobertura <- cuota_usd * trm_forward * 4 * cobertura_porcentaje + cuota_usd * escenarios_trm * 4 * (1 - cobertura_porcentaje)
df_comparacion <- data.frame( Escenario = rep(nombres_escenarios, 2), Tipo = rep(c(“Sin Cobertura”, “Con Cobertura (75%)”), each = 4), Cuota_Anual_Millones = c(cuota_sin_cobertura / 1e6, cuota_con_cobertura / 1e6) )
ggplot(df_comparacion, aes(x = Escenario, y = Cuota_Anual_Millones, fill = Tipo)) + geom_bar(stat = “identity”, position = position_dodge(width = 0.9)) + geom_text(aes(label = paste0(“$”, format(round(Cuota_Anual_Millones, 0), big.mark = “.”), “M”)), position = position_dodge(width = 0.9), vjust = -0.5, size = 3) + scale_fill_manual(values = c(“Sin Cobertura” = “orange”, “Con Cobertura (75%)” = “purple”)) + labs(title = “Efecto de la Cobertura con Forwards en Cuota Anual”, x = “Escenario de TRM”, y = “Cuota Anual (Millones de COP)”, fill = “Estrategia”) + theme_minimal() + theme(legend.position = “bottom”)
# Crear matriz de probabilidades por rango de TRM breaks_trm <- seq(floor(min(trm_mes_12)), ceiling(max(trm_mes_12)), length.out = 15) hist_data <- hist(trm_mes_12, breaks = breaks_trm, plot = FALSE)
df_heatmap <- data.frame( TRM_Rango = paste0(“\(", format(round(hist_data\)breaks[-length(hist_data\(breaks)], 0), big.mark = "."), "-", "\)”, format(round(hist_data\(breaks[-1], 0), big.mark = ".")), Probabilidad = hist_data\)counts / n_simulaciones * 100 )
df_heatmap_top <- df_heatmap[order(df_heatmap$Probabilidad, decreasing = TRUE), ][1:5, ]
ggplot(df_heatmap_top, aes(x = reorder(TRM_Rango, -Probabilidad), y = 1, fill = Probabilidad)) + geom_tile() + scale_fill_gradient(low = “lightblue”, high = “darkred”, name = “Probabilidad (%)”) + geom_text(aes(label = paste0(round(Probabilidad, 2), “%”)), size = 3.5, fontface = “bold”) + labs(title = “Top 5 Rangos de TRM con Mayor Probabilidad”, subtitle = “Distribucion de probabilidad a 12 meses (10,000 simulaciones)”, x = “Rango de TRM (COP/USD)”, y = ““) + theme_minimal() + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank(), axis.text.x = element_text(angle = 45, hjust = 1, size = 10), plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5))
trimestres <- 1:40 cuota_base <- rep(cuota_usd * percentiles_valores[“50%”] / 1e6, 40) cuota_extremo <- rep(cuota_usd * percentiles_valores[“95%”] / 1e6, 40) cuota_catastrofe <- rep(cuota_usd * percentiles_valores[“99%”] / 1e6, 40)
df_evolucion <- data.frame( Trimestre = rep(trimestres, 3), Cuota_Millones = c(cuota_base, cuota_extremo, cuota_catastrofe), Escenario = rep(c(“Escenario Base (P50)”, “Escenario Extremo (P95)”, “Escenario Catastrófico (P99)”), each = 40) )
ggplot(df_evolucion, aes(x = Trimestre, y = Cuota_Millones, color = Escenario, group = Escenario)) + geom_line(size = 1.2) + scale_color_manual(values = c(“Escenario Base (P50)” = “darkgreen”, “Escenario Extremo (P95)” = “orange”, “Escenario Catastrofico (P99)” = “gray”)) + labs(title = “Evolucion de la Cuota Trimestral”, subtitle = “Escenarios extremos vs escenario base”, x = “Trimestre del Credito”, y = “Cuota Trimestral (MM de COP)”) + theme_minimal() + theme(legend.position = “bottom”)
cuota_anual_base <- cuota_usd * percentiles_valores[“50%”] * 4 cuota_anual_extremo <- cuota_usd * percentiles_valores[“95%”] * 4 cuota_anual_catastrofe <- cuota_usd * percentiles_valores[“99%”] * 4
metricas_clave <- data.frame( Metrica = c(“Cuota Anual - Escenario Base”, “Cuota Anual - Escenario Extremo (P95)”, “Cuota Anual - Escenario Catastrofico (P99)”, “VaR Anual (95% confianza)”, “VaR Anual (99% confianza)”, “Ahorro Estimado con Cobertura (P95)”, “Reserva de Liquidez Recomendada”, “Umbral de Alerta TRM”, “Umbral de Ejecucion TRM”), Valor = c(paste0(“\(", format(round(cuota_anual_base / 1e6, 0), big.mark = "."), "M"), paste0("\)”, format(round(cuota_anual_extremo / 1e6, 0), big.mark = “.”), “M”), paste0(“\(", format(round(cuota_anual_catastrofe / 1e6, 0), big.mark = "."), "M"), paste0("\)”, format(round(var_resultados\(VaR_Anual_COP[2] / 1e6, 0), big.mark = "."), "M"), paste0("\)“, format(round(var_resultados\(VaR_Anual_COP[3] / 1e6, 0), big.mark = "."), "M"), paste0("\)”, format(round(beneficio_cobertura / 1e6, 0), big.mark = “.”), “M”), paste0(“\(", format(round(cuota_usd * percentiles_valores["95%"] * 2 / 1e6, 0), big.mark = "."), "M"), paste0("\)”, format(round(percentiles_valores[“75%”], 0), big.mark = “.”), ” COP/USD”), paste0(“$”, format(round(percentiles_valores[“90%”], 0), big.mark = “.”), ” COP/USD”)) )
kable(metricas_clave, caption = “Tabla 8: Metricas clave para Gestion de Riesgo”, format.args = list(big.mark = “.”)) %>% kable_styling(bootstrap_options = c(“striped”, “hover”, “condensed”), full_width = FALSE)