Introducción

Este taller práctico simula una situación real donde, como inversores, requerimos financiar la adquisición de maquinaria amarilla por 300 millones de pesos a través de un crédito estadounidense a 10 años con sistema de pago francés. Dado que los pagos del crédito se realizarán en dólares mientras nuestros ingresos son en pesos colombianos, implementaremos una estrategia de cobertura utilizando futuros de TRM. Se realizará una cobertura del 75% de la inversión durante los últimos cuatro años del crédito, período en el cual se anticipa mayor volatilidad cambiaria y una posible apreciación del dólar. A través de este ejercicio, aplicaremos conceptos fundamentales de derivados financieros, específicamente el uso de futuros como instrumento de cobertura, empleando datos históricos de la Bolsa de Valores de Colombia y replicando las condiciones reales del mercado financiero.

options(repos = c(CRAN = "https://cran.rstudio.com/"))
install.packages("citmre")
## Installing package into 'C:/Users/jmora/AppData/Local/R/win-library/4.3'
## (as 'lib' is unspecified)
## package 'citmre' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\jmora\AppData\Local\Temp\RtmpKmpovb\downloaded_packages
library(citmre)
## Warning: package 'citmre' was built under R version 4.3.3
rmre_data()
##               rmre
## 1991-12-02  643.42
## 1991-12-03  639.22
## 1991-12-04  635.70
## 1991-12-05  631.51
## 1991-12-06  627.16
## 1991-12-09  638.06
## 1991-12-10  622.92
## 1991-12-11  627.46
## 1991-12-12  633.09
## 1991-12-13  632.35
##        ...        
## 2025-09-09 3945.29
## 2025-09-10 3919.13
## 2025-09-11 3921.58
## 2025-09-12 3903.18
## 2025-09-15 3906.24
## 2025-09-16 3897.57
## 2025-09-17 3881.92
## 2025-09-18 3880.01
## 2025-09-19 3892.45
## 2025-09-22 3891.19
data <- rmre_data(start_date = "2015-01-01", end_date = "2025-09-15")
head(data)
##               rmre
## 2015-01-02 2392.46
## 2015-01-05 2383.37
## 2015-01-06 2412.82
## 2015-01-07 2452.11
## 2015-01-08 2434.31
## 2015-01-09 2405.03
data_m <- rmre_data(start_date = "2015-01-01", end_date = "2025-09-15", frequency = 12)
head(data_m)
##               rmre
## 2015-01-31 2397.35
## 2015-02-28 2484.58
## 2015-03-31 2598.36
## 2015-04-30 2388.06
## 2015-05-31 2533.79
## 2015-06-30 2598.68
data_log <- rmre_data(start_date = "2015-01-01", end_date = "2025-09-15", log_return = TRUE)
head(data_log)
##               log_return
## 2015-01-05 -0.0038066728
## 2015-01-06  0.0122807354
## 2015-01-07  0.0161526905
## 2015-01-08 -0.0072855296
## 2015-01-09 -0.0121009713
## 2015-01-13  0.0006982921
plot(data_m)

Análisis fundamental de la Tasa Representativa del Mercado (TRM) en Colombia

Marco Conceptual de la TRM

La TRM constituye el promedio aritmético de las cotizaciones ponderadas resultantes de las transacciones de compra y venta de dólares estadounidenses por pesos colombianos, ejecutadas por entidades del sistema financiero nacional incluyendo bancos comerciales, corporaciones financieras, comisionistas de bolsa, compañías de financiamiento, la FDN y Bancoldex. Estas operaciones se caracterizan por su liquidación inmediata en ambas divisas el mismo día de contratación (Banco de la República de Colombia, s.f.).

Contexto Macroeconómico Actual (2025)

En agosto de 2025, la inflación interanual en Colombia fue de 4,85%, inferior al 4,89% observado en julio. La persistencia de la inflación por encima de la meta del 3% anual establecida por la Junta Directiva del Banco de la República, mantiene la presión sobre la política monetaria y genera expectativas de un proceso de convergencia más lento de lo inicialmente previsto. La economía colombiana muestra una recuperación moderada, con un crecimiento estimado del 2,6% en 2025 y una proyección de 3,4% para 2026. Esta mejora estaría sustentada en una política monetaria más flexible y en la reducción gradual de la inflación (Banco de la República, Informe de Política Monetaria, 2025).

Análisis de Tendencias Recientes

Durante la ventana temporal comprendida entre el segundo semestre de 2024 y la fecha actual, la TRM ha exhibido una tendencia a la baja, reflejando un fortalecimiento sostenido del peso colombiano frente al dólar estadounidense. Esta dinámica,ha estado influenciada por factores de política monetaria tanto domésticos como internacionales.

En la búsqueda de controlar la inflación y hacer más atractivo el peso colombiano para la inversión extranjera de corto plazo, el BanRep ha mantenido la tasa de interés de política monetaria en un nivel relativamente alto (9,25% a partir de mayo), lo que ha contribuido a la apreciación del peso, por otro lado, el Sistema de la Reserva Federal (FED) ha implemetado recortes graduales en sus tasas de referencia, debilitando la posición relativa del dólar.

Proyecciones y Expectativas del Mercado

Los resultados de la Encuesta Mensual de Expectativas (EME) del Banco de la República correspondiente a julio de 2025 revelan una perspectiva contradictoria pero reveladora: el consentimiento del mercado anticipa una depreciación gradual pero sostenida del peso colombiano, proyectando que la TRM alcance niveles de $4,180-$4,200 hacia finales de 2026, lo que representa una depreciación aproximada del 3.4% anual desde los niveles actuales. Sin embargo, la alta dispersión de los datos analizados de la encuesta, resalta que el futuro de la TRM dependerá de la evolución de múltiples factores económicos, tanto globales como nacionales.

Simulación BMG mensual

Para la construcción de la simulación se tomará el histórico de la TRM de los últimos 10 años, estos datos son obtenidos desde el aplicativo RStudio a través de un paquete llamado Citmre, con histórico de la TRM, se realiza la conversión de los datos a periodos mensuales.

options ( scipen = 999)
set.seed (2134)
sigma <- sd(data_log$log_return)
mu <- mean(data_log$log_return)
s0 <- tail(data_m$rmre, 1)
N =10000
T =120
vec = rep (s0 ,N)
mb= matrix ( ncol =N, nrow =T)
mb [1 ,]= vec

for (i in 1:N) {
  for (t in 2:T) {
    mb[t,i]= mb [(t -1) ,i]* exp ((mu -(0.5 *( sigma ^2) ))*(1/ 252) +
                                    sigma *(1/ (252) ^(1 /2))* qnorm ( runif (1, min = 0, max = 1)))
  }
}

df_mb <- data.frame(Mes = 1:T, TRM = as.vector(mb))


matplot (mb , type ="l")

q1= quantile (mb[T ,], 0.975)
q2= quantile (mb[T ,], 0.025)
plot ( density (mb[T ,]) , ylab ="", xlab ="",
       main =" Distribución normal ", lwd = 3)
abline (h=NULL , v=q1 , col = " blue ",lwd = 2)
abline (h=NULL , v=q2 , col = " green ",lwd = 2)

Los resultados de la simulación a 10 años revelan un comportamiento relativamente estable de la TRM con un rendimiento mensual promedio de 0.019331% (equivalente a aproximadamente 0.23% anual), lo que sugiere una depreciación muy gradual del peso colombiano frente al dólar estadounidense. La volatilidad mensual de 0.8542% (aproximadamente 2.96% anualizada) indica un nivel de fluctuación moderado, esta volatilidad relativamente contenida sugiere que, aunque existen períodos de mayor incertidumbre cambiaria, la TRM no presenta movimientos extremos sostenidos, lo que valida la efectividad de las políticas de estabilización del Banco de la República y la robustez del régimen cambiario flexible.

Simulación del crédito en EEUU

Para la compra de la maquinaria amarilla el valor de la TRM del día 15 de septiembre de 2025, la cual fue de 3.906,24 y un valor total a financiar de 65280,17 USD, equivalente al 85% del crédito total, este queda con cuotas fijas mensuales a 10 años y se utilizó una tasa comercial del 16.99% APR (Citibank).

Posterior a la simulación del comportamiento de la TRM, se seleccionaron tres escenarios clave que servirán como base para estrategias de cobertura con el futuro de TRX.

Simulación de Mayor Dispersión

En este se evidencia una máxima volatilidad y una tendencia alcista por lo cual se asumiría una posición larga para tener una mejor cobertura en futuros TRX.

Simulación de Menor Dispersión

En este se evidencia una mínima volatilidad y una tendencia bajista por lo cual se asumiría una posición corta para tener una mejor cobertura en futuros TRM contra depreciación del dólar.

Simulación Promedio Escenario más probable estadísticamente y en este es en que se va a hacer un análisis más profundo y con base a este se van a generar una estrategia de cobertura y de gestión de riesgo con base a los datos generados en las simulaciones del futuro en TRX. Al tener presente los dos escenarios extremos se busca mostrar un análisis de más objetivo al no estar en los límites, pero teniéndolos presentes en medio del estudio realizado.

# Parámetros del préstamo
P <- (300000000/3906.24)*0.85       

# tasa citibank 16.99% APR
E_A <- (1+0.1699/12)^12-1

i_annual <- E_A    
n_months <- 120      

i <- (1+E_A)^(1/12)-1 
n <- n_months 

cuota <- P * (i * (1 + i)^n) / ((1 + i)^n - 1)

tabla <- data.frame(
  Periodo = 1:n,
  Cuota = rep(round(cuota, 2), n),
  Interes = numeric(n),
  Amortizacion = numeric(n),
  Saldo = numeric(n)
)

saldo_restante <- P

for (t in 1:n) {
  interes <- round(saldo_restante * i, 2)
  amortizacion <- round(cuota - interes, 2)
  saldo_restante <- round(saldo_restante - amortizacion, 2)
  
  tabla$Interes[t] <- interes
  tabla$Amortizacion[t] <- amortizacion
  tabla$Saldo[t] <- ifelse(saldo_restante < 0, 0, saldo_restante)
}

print(tabla)
##     Periodo   Cuota Interes Amortizacion    Saldo
## 1         1 1134.14  924.26       209.88 65070.29
## 2         2 1134.14  921.29       212.85 64857.44
## 3         3 1134.14  918.27       215.87 64641.57
## 4         4 1134.14  915.22       218.92 64422.65
## 5         5 1134.14  912.12       222.02 64200.63
## 6         6 1134.14  908.97       225.17 63975.46
## 7         7 1134.14  905.79       228.35 63747.11
## 8         8 1134.14  902.55       231.59 63515.52
## 9         9 1134.14  899.27       234.87 63280.65
## 10       10 1134.14  895.95       238.19 63042.46
## 11       11 1134.14  892.58       241.56 62800.90
## 12       12 1134.14  889.16       244.98 62555.92
## 13       13 1134.14  885.69       248.45 62307.47
## 14       14 1134.14  882.17       251.97 62055.50
## 15       15 1134.14  878.60       255.54 61799.96
## 16       16 1134.14  874.98       259.16 61540.80
## 17       17 1134.14  871.32       262.82 61277.98
## 18       18 1134.14  867.59       266.55 61011.43
## 19       19 1134.14  863.82       270.32 60741.11
## 20       20 1134.14  859.99       274.15 60466.96
## 21       21 1134.14  856.11       278.03 60188.93
## 22       22 1134.14  852.17       281.97 59906.96
## 23       23 1134.14  848.18       285.96 59621.00
## 24       24 1134.14  844.13       290.01 59330.99
## 25       25 1134.14  840.03       294.11 59036.88
## 26       26 1134.14  835.86       298.28 58738.60
## 27       27 1134.14  831.64       302.50 58436.10
## 28       28 1134.14  827.36       306.78 58129.32
## 29       29 1134.14  823.01       311.13 57818.19
## 30       30 1134.14  818.61       315.53 57502.66
## 31       31 1134.14  814.14       320.00 57182.66
## 32       32 1134.14  809.61       324.53 56858.13
## 33       33 1134.14  805.02       329.12 56529.01
## 34       34 1134.14  800.36       333.78 56195.23
## 35       35 1134.14  795.63       338.51 55856.72
## 36       36 1134.14  790.84       343.30 55513.42
## 37       37 1134.14  785.98       348.16 55165.26
## 38       38 1134.14  781.05       353.09 54812.17
## 39       39 1134.14  776.05       358.09 54454.08
## 40       40 1134.14  770.98       363.16 54090.92
## 41       41 1134.14  765.84       368.30 53722.62
## 42       42 1134.14  760.62       373.52 53349.10
## 43       43 1134.14  755.33       378.81 52970.29
## 44       44 1134.14  749.97       384.17 52586.12
## 45       45 1134.14  744.53       389.61 52196.51
## 46       46 1134.14  739.02       395.12 51801.39
## 47       47 1134.14  733.42       400.72 51400.67
## 48       48 1134.14  727.75       406.39 50994.28
## 49       49 1134.14  721.99       412.15 50582.13
## 50       50 1134.14  716.16       417.98 50164.15
## 51       51 1134.14  710.24       423.90 49740.25
## 52       52 1134.14  704.24       429.90 49310.35
## 53       53 1134.14  698.15       435.99 48874.36
## 54       54 1134.14  691.98       442.16 48432.20
## 55       55 1134.14  685.72       448.42 47983.78
## 56       56 1134.14  679.37       454.77 47529.01
## 57       57 1134.14  672.93       461.21 47067.80
## 58       58 1134.14  666.40       467.74 46600.06
## 59       59 1134.14  659.78       474.36 46125.70
## 60       60 1134.14  653.06       481.08 45644.62
## 61       61 1134.14  646.25       487.89 45156.73
## 62       62 1134.14  639.34       494.80 44661.93
## 63       63 1134.14  632.34       501.80 44160.13
## 64       64 1134.14  625.23       508.91 43651.22
## 65       65 1134.14  618.03       516.11 43135.11
## 66       66 1134.14  610.72       523.42 42611.69
## 67       67 1134.14  603.31       530.83 42080.86
## 68       68 1134.14  595.79       538.35 41542.51
## 69       69 1134.14  588.17       545.97 40996.54
## 70       70 1134.14  580.44       553.70 40442.84
## 71       71 1134.14  572.60       561.54 39881.30
## 72       72 1134.14  564.65       569.49 39311.81
## 73       73 1134.14  556.59       577.55 38734.26
## 74       74 1134.14  548.41       585.73 38148.53
## 75       75 1134.14  540.12       594.02 37554.51
## 76       76 1134.14  531.71       602.43 36952.08
## 77       77 1134.14  523.18       610.96 36341.12
## 78       78 1134.14  514.53       619.61 35721.51
## 79       79 1134.14  505.76       628.38 35093.13
## 80       80 1134.14  496.86       637.28 34455.85
## 81       81 1134.14  487.84       646.30 33809.55
## 82       82 1134.14  478.69       655.45 33154.10
## 83       83 1134.14  469.41       664.73 32489.37
## 84       84 1134.14  460.00       674.14 31815.23
## 85       85 1134.14  450.45       683.69 31131.54
## 86       86 1134.14  440.77       693.37 30438.17
## 87       87 1134.14  430.95       703.19 29734.98
## 88       88 1134.14  421.00       713.14 29021.84
## 89       89 1134.14  410.90       723.24 28298.60
## 90       90 1134.14  400.66       733.48 27565.12
## 91       91 1134.14  390.28       743.86 26821.26
## 92       92 1134.14  379.74       754.40 26066.86
## 93       93 1134.14  369.06       765.08 25301.78
## 94       94 1134.14  358.23       775.91 24525.87
## 95       95 1134.14  347.25       786.89 23738.98
## 96       96 1134.14  336.10       798.04 22940.94
## 97       97 1134.14  324.81       809.33 22131.61
## 98       98 1134.14  313.35       820.79 21310.82
## 99       99 1134.14  301.73       832.41 20478.41
## 100     100 1134.14  289.94       844.20 19634.21
## 101     101 1134.14  277.99       856.15 18778.06
## 102     102 1134.14  265.87       868.27 17909.79
## 103     103 1134.14  253.57       880.57 17029.22
## 104     104 1134.14  241.11       893.03 16136.19
## 105     105 1134.14  228.46       905.68 15230.51
## 106     106 1134.14  215.64       918.50 14312.01
## 107     107 1134.14  202.63       931.51 13380.50
## 108     108 1134.14  189.45       944.69 12435.81
## 109     109 1134.14  176.07       958.07 11477.74
## 110     110 1134.14  162.51       971.63 10506.11
## 111     111 1134.14  148.75       985.39  9520.72
## 112     112 1134.14  134.80       999.34  8521.38
## 113     113 1134.14  120.65      1013.49  7507.89
## 114     114 1134.14  106.30      1027.84  6480.05
## 115     115 1134.14   91.75      1042.39  5437.66
## 116     116 1134.14   76.99      1057.15  4380.51
## 117     117 1134.14   62.02      1072.12  3308.39
## 118     118 1134.14   46.84      1087.30  2221.09
## 119     119 1134.14   31.45      1102.69  1118.40
## 120     120 1134.14   15.83      1118.31     0.09
cat("\nTotal pagado:", round(sum(tabla$Cuota), 2), "\n")
## 
## Total pagado: 136096.8
cat("Intereses totales:", round(sum(tabla$Interes), 2), "\n")
## Intereses totales: 70816.72
final_prices <- mb[T, ]
indice_max <- which.max(final_prices)
trayectoria_maxima <- mb[, indice_max]
indice_min <- which.min(final_prices)
trayectoria_minima <- mb[, indice_min]
trayectoria_media <- rowMeans(mb)
trayectoria_mediana <- apply(mb, 1, median)
df_trayectorias <- data.frame(
  Tiempo = 1:T,
  Maxima = trayectoria_maxima,
  Media = trayectoria_media,
  Mediana = trayectoria_mediana,
  Minima = trayectoria_minima
)

# Mostrar información de las trayectorias extremas
cat("\n=== TRAYECTORIAS PRINCIPALES ===\n")
## 
## === TRAYECTORIAS PRINCIPALES ===
cat("Trayectoria MÁXIMA (Simulación", indice_max, "):\n")
## Trayectoria MÁXIMA (Simulación 1662 ):
cat("  - Precio final:", round(trayectoria_maxima[T], 2), "\n")
##   - Precio final: 3992.31
cat("  - Rendimiento total:", round((trayectoria_maxima[T]/s0 - 1)*100, 2), "%\n")
##   - Rendimiento total: 2.2 %
cat("\nTrayectoria MÍNIMA (Simulación", indice_min, "):\n")
## 
## Trayectoria MÍNIMA (Simulación 6163 ):
cat("  - Precio final:", round(trayectoria_minima[T], 2), "\n")
##   - Precio final: 3822.06
cat("  - Rendimiento total:", round((trayectoria_minima[T]/s0 - 1)*100, 2), "%\n")
##   - Rendimiento total: -2.16 %
cat("\nTrayectoria MEDIA:\n")
## 
## Trayectoria MEDIA:
cat("  - Precio final:", round(trayectoria_media[T], 2), "\n")
##   - Precio final: 3906.57
cat("  - Rendimiento total:", round((trayectoria_media[T]/s0 - 1)*100, 2), "%\n")
##   - Rendimiento total: 0.01 %
write.csv(df_trayectorias, "trayectorias_principales.csv", row.names = FALSE)
cat("\nTrayectorias guardadas en: trayectorias_principales.csv\n")
## 
## Trayectorias guardadas en: trayectorias_principales.csv
library(ggplot2)
library(tidyr)
## Warning: package 'tidyr' was built under R version 4.3.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
sample_paths <- 30  
sample_indices <- sample(1:N, sample_paths)


df_sample <- data.frame(
  Time = rep(1:T, sample_paths),
  Price = as.vector(mb[, sample_indices]),
  Path = rep(paste("Path", sample_indices), each = T),
  Type = "Sample"
)

df_main <- data.frame(
  Time = rep(1:T, 4),
  Price = c(trayectoria_maxima, trayectoria_media, trayectoria_mediana, trayectoria_minima),
  Path = rep(c("Máxima", "Media", "Mediana", "Mínima"), each = T),
  Type = rep(c("Máxima", "Media", "Mediana", "Mínima"), each = T)
)

df_all <- rbind(df_sample, df_main)

# Gráfico 1: Todas las trayectorias con énfasis en las principales
p1 <- ggplot() +
  
  geom_line(data = df_sample, aes(x = Time, y = Price, group = Path), 
            alpha = 0.2, color = "lightgray", size = 0.3) +
  
  geom_line(data = subset(df_main, Path == "Máxima"), 
            aes(x = Time, y = Price), color = "green", size = 1.2) +
  geom_line(data = subset(df_main, Path == "Media"), 
            aes(x = Time, y = Price), color = "blue", size = 1.2) +
  geom_line(data = subset(df_main, Path == "Mediana"), 
            aes(x = Time, y = Price), color = "orange", size = 1.2) +
  geom_line(data = subset(df_main, Path == "Mínima"), 
            aes(x = Time, y = Price), color = "red", size = 1.2) +
  
  geom_hline(yintercept = s0, linetype = "dashed", color = "black", size = 0.8) +
  labs(title = "Trayectorias de Simulación Monte Carlo",
       subtitle = paste("Trayectorias principales de", N, "simulaciones -", T, "días"),
       x = "Días",
       y = "Precio") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    plot.subtitle = element_text(hjust = 0.5, size = 10),
    legend.position = "bottom"
  ) +
  
  annotate("text", x = T*0.7, y = max(df_main$Price)*0.9, 
           label = "Verde: Máxima\nAzul: Media\nNaranja: Mediana\nRojo: Mínima", 
           size = 3, hjust = 0)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
df_main_wide <- df_trayectorias %>%
  pivot_longer(cols = c(Maxima, Media, Mediana, Minima), 
               names_to = "Trayectoria", values_to = "Precio")

p2 <- ggplot(df_main_wide, aes(x = Tiempo, y = Precio, color = Trayectoria)) +
  geom_line(size = 1.2, alpha = 0.8) +
  scale_color_manual(values = c("Maxima" = "green", "Media" = "blue", 
                               "Mediana" = "orange", "Minima" = "red")) +
  geom_hline(yintercept = s0, linetype = "dashed", color = "black", alpha = 0.6) +
  labs(title = "Trayectorias Principales - Simulación Monte Carlo",
       subtitle = "Comparación de trayectorias extremas y central",
       x = "Días",
       y = "Precio",
       color = "Tipo de\nTrayectoria") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    plot.subtitle = element_text(hjust = 0.5, size = 10),
    legend.position = "right"
  )

p3 <- ggplot(data.frame(Price = final_prices), aes(x = Price)) +
  geom_histogram(bins = 50, fill = "lightblue", color = "darkblue", alpha = 0.7) +
  geom_vline(xintercept = mean(final_prices), color = "blue", linetype = "dashed", size = 1) +
  geom_vline(xintercept = s0, color = "black", linetype = "dashed", size = 1) +
  geom_vline(xintercept = max(final_prices), color = "green", linetype = "dashed", size = 1) +
  geom_vline(xintercept = min(final_prices), color = "red", linetype = "dashed", size = 1) +
  labs(title = "Distribución de Precios Finales",
       subtitle = paste("Después de", T, "días -", N, "simulaciones"),
       x = "Precio Final",
       y = "Frecuencia") +
  theme_minimal() +
  annotate("text", x = max(final_prices)*0.8, y = Inf, vjust = 2,
           label = "Negro: Inicial\nAzul: Media\nVerde: Máximo\nRojo: Mínimo", 
           size = 3)

print(p1)

print(p2)

print(p3)

get_trayectorias <- function() {
  list(
    maxima = trayectoria_maxima,
    minima = trayectoria_minima,
    media = trayectoria_media,
    mediana = trayectoria_mediana,
    datos_completos = df_trayectorias
  )
}

var_95 <- quantile(final_prices, 0.05)
cat("\nValue at Risk (5%):", round(var_95, 2), "\n")
## 
## Value at Risk (5%): 3869.52
#Credito trayectoria max
trayectoria_maxima <- as.vector(trayectoria_maxima)
amortizacionCOP <- tabla
columnas_amortizacion <- c("Cuota", "Interes", "Amortizacion", "Saldo")
amortizacionCOP[ , columnas_amortizacion] <- tabla[ , columnas_amortizacion] * trayectoria_maxima

df_pago <- data.frame(Mes = 1:nrow(amortizacionCOP),Pago = amortizacionCOP[, "Cuota"])

ggplot(df_pago, aes(x = Mes, y = trayectoria_maxima)) + 
  geom_line(color = "#1f4e79", linewidth = 1.5, alpha = 0.8) +
  geom_point(color = "#1f4e79", size = 1, alpha = 0.9) +
  labs(title = "Comportamiento del Pago Mensual",
       subtitle = "Evolución mensual de pagos en pesos colombianos",
       x = "Mes", 
       y = "Pago (COP)",
       caption = "Fuente: Datos internos") +
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = " COP", big.mark = ".", decimal.mark = ",")
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", color = "#2c3e50"),
    plot.subtitle = element_text(hjust = 0.5, size = 21, color = "#7f8c8d"),
    axis.title = element_text(size = 21, face = "bold"),
    axis.text = element_text(size = 10),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_line(color = "#ecf0f1", size = 0.5),
    plot.background = element_rect(fill = "#ffffff", color = NA),
    panel.background = element_rect(fill = "#ffffff", color = NA)
  )
## Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

#Credito trayectoria min
trayectoria_minima <- as.vector(trayectoria_minima)
amortizacionCOP <- tabla
columnas_amortizacion_min <- c("Cuota", "Interes", "Amortizacion", "Saldo")
amortizacionCOP[ , columnas_amortizacion_min] <- tabla[ , columnas_amortizacion_min] * trayectoria_minima

df_pago <- data.frame(Mes = 1:nrow(amortizacionCOP),Pago = amortizacionCOP[, "Cuota"])

ggplot(df_pago, aes(x = Mes, y = trayectoria_minima)) + 
  geom_line(color = "#1f4e79", linewidth = 1.5, alpha = 0.8) +
  geom_point(color = "#1f4e79", size = 1, alpha = 0.9) +
  labs(title = "Comportamiento del Pago Mensual",
       subtitle = "Evolución mensual de pagos en pesos colombianos",
       x = "Mes", 
       y = "Pago (COP)",
       caption = "Fuente: Datos internos") +
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = " COP", big.mark = ".", decimal.mark = ",")
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", color = "#2c3e50"),
    plot.subtitle = element_text(hjust = 0.5, size = 21, color = "#7f8c8d"),
    axis.title = element_text(size = 21, face = "bold"),
    axis.text = element_text(size = 10),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_line(color = "#ecf0f1", size = 0.5),
    plot.background = element_rect(fill = "#ffffff", color = NA),
    panel.background = element_rect(fill = "#ffffff", color = NA)
  )

# Credito trayectoria media
trayectoria_media <- as.vector(trayectoria_media)
amortizacionCOP <- tabla
columnas_amortizacion_media <- c("Cuota", "Interes", "Amortizacion", "Saldo")
amortizacionCOP[ , columnas_amortizacion_media] <- tabla[ , columnas_amortizacion_media] * trayectoria_media

df_pago <- data.frame(Mes = 1:nrow(amortizacionCOP),Pago = amortizacionCOP[, "Cuota"])

ggplot(df_pago, aes(x = Mes, y = trayectoria_media)) + 
  geom_line(color = "#1f4e79", linewidth = 1.5, alpha = 0.8) +
  geom_point(color = "#1f4e79", size = 1, alpha = 0.9) +
  labs(title = "Comportamiento del Pago Mensual",
       subtitle = "Evolución mensual de pagos en pesos colombianos",
       x = "Mes", 
       y = "Pago (COP)",
       caption = "Fuente: Datos internos") +
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = " COP", big.mark = ".", decimal.mark = ",")
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold", color = "#2c3e50"),
    plot.subtitle = element_text(hjust = 0.5, size = 21, color = "#7f8c8d"),
    axis.title = element_text(size = 21, face = "bold"),
    axis.text = element_text(size = 10),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_line(color = "#ecf0f1", size = 0.5),
    plot.background = element_rect(fill = "#ffffff", color = NA),
    panel.background = element_rect(fill = "#ffffff", color = NA)
  )

Simulación con el Movimiento Browniano para el precio del Futuro

El cálculo del precio teórico futuro, derivado de la relación entre tasas nacionales e internacionales, constituye la base metodológica para establecer los parámetros iniciales de la simulación.

Para el cálculo de este se utilizó para el caso de Colombia la tasa promedio ponderada de la superintendencia financiera de Colombia con corte al 12 de septiembre de 2025. Superintendencia Financiera de Colombia. (s.f.). En el caso de EEUU la tasa trabajada para la toma del crédito.

library(dplyr)
library(ggplot2)

library(readxl) AlumSpot <- read_excel(“D:/ESTOCASTICOS/TEMA 80%/AlumSpot.xlsx”)

C:/Users/jmora/OneDrive/Documentos/DERIVAD

“C:/Users/jmora/OneDrive/Documentos/DERIVADOS FROS/futuros.xlsx”

library(readxl)
## Warning: package 'readxl' was built under R version 4.3.3
futuros <- read_excel("C:/Users/jmora/OneDrive/Documentos/DERIVADOS FROS/futuros.xlsx")

# Parámetros
rn <- (1+0.198)^(1/21)-1  # Tasa nacional (19.8)
re <- i  # Tasa extranjera semestral (4.13%)

# Precio inicial (último precio)
F0 <- as.numeric(tail(futuros[, "prom_precio_cierre"], 1))  # ✅ tail() en lugar de last()

# Precio teórico del futuro
FT <- F0 * ((1 + rn) / (1 + re))^(1/21)

cat("El precio teórico del futuro basado en la paridad de tasas es:", round(FT, 4), "\n")
## El precio teórico del futuro basado en la paridad de tasas es: 3949.103
# Calcular parámetros usando datos de futuros
retornos_trm <- diff(log(futuros$prom_precio_cierre))
r_trm <- exp(mean(retornos_trm)) - 1
sigma_trm <- sd(retornos_trm)

# Mensualizar los parámetros
r_trm <- r_trm*21
sigma_trm <- sqrt(21)*sigma_trm

#Simulacion TRM
T<-120 
dt<-1/21
s0 <- FT  
N<-1
set.seed(2032)
vec_trm=rep(s0,N)
mb_trm=matrix(ncol=N,nrow = T)
mb_trm[1,]=vec_trm

for (i in 1:N) {
  for (t in 2:T) {
    mb_trm[t,i]=mb_trm[(t-1),i]*exp((r_trm-(0.5*(sigma_trm^2)))*(dt) +
                              sigma_trm*sqrt(dt)*rnorm(1))
  }
}

df_trm <- data.frame(Mes = 1:T, TRM = as.vector(mb_trm))

ggplot(df_trm, aes(x = Mes, y = TRM)) + geom_line(color = "blue", size = 1) +
  labs(title = "Trayectoria simulada de TRM", x = "Mes", y = "TRM") +
  theme_minimal()+theme(plot.title = element_text(hjust = 0.5))

## Cobertura con futuros

Aunque el ejercicio sugiere un nivel de apalancamiento del 75% mediante futuros, esta práctica busca determinar si un porcentaje inferior puede proporcionar una cobertura adecuada sin comprometer significativamente la liquidez corporativa. Para este propósito, se emplearán contratos TRX en lugar de TRM, considerando que estos últimos, con un tamaño contractual de 50,000 USD, impondrían un riesgo excesivo dada la magnitud de cada posición, mientras que los contratos TRX ofrecen mayor granularidad para calibrar la estrategia de cobertura de manera más precisa.

Se toma como valor nominal el valor de la TRM del 19 de septiembre, dato tomado de la BVC. El margen de mantenimiento será del 50% trabajandolo como un estándar dentro de los ejercicios de futuros y el margen incial será del 6,3% basado en la información de apalancamiento de futuros proporcionado por la BVC.

cobertura<-P*0.75
trx<-1000
numero_contratos<-round(cobertura/trx)
print(numero_contratos)
## [1] 49
# Asumiendo los datos de la BVC
nominal<-3891.19 

# 1. Criterio de Exposición Total
exp_total<-numero_contratos*nominal

# 2. Criterio de Margen Inicial
margen_inicial<-0.063*exp_total

# 3. Criterio de Margen de Mantenimiento
margen_mantenimiento<-0.5*margen_inicial

print(paste("Exposición Total:", exp_total))
## [1] "Exposición Total: 190668.31"
print(paste("Margen Inicial:", margen_inicial))
## [1] "Margen Inicial: 12012.10353"
print(paste("Margen de Mantenimiento:", margen_mantenimiento))
## [1] "Margen de Mantenimiento: 6006.051765"
generar_tabla_futuros <- function(tiempo, precios_futuros, precio_futuro_inicial, valor_contrato, margen_inicial, margen_mantenimiento_porcentaje = 0.5, posicion = "larga") {
  
  margen_mantenimiento <- margen_inicial * margen_mantenimiento_porcentaje
  
  tabla_futuros <- data.frame(
    T = tiempo,
    Precio.Futuro = c(precio_futuro_inicial, precios_futuros),
    Dif.PF = numeric(length(tiempo)),
    Liquidacion = numeric(length(tiempo)),
    Margen = numeric(length(tiempo)),
    Margin.Call = character(length(tiempo))
  )
  
  tabla_futuros$Margen[1] <- margen_inicial
  tabla_futuros$Dif.PF[1] <- 0
  tabla_futuros$Liquidacion[1] <- 0
  tabla_futuros$Margin.Call[1] <- 0
  
  for (i in 2:nrow(tabla_futuros)) {
    precio_hoy <- tabla_futuros$Precio.Futuro[i]
    precio_ayer <- tabla_futuros$Precio.Futuro[i-1]
    
    if (posicion == "larga") {
      tabla_futuros$Dif.PF[i] <- precio_hoy - precio_ayer
    } else if (posicion == "corta") {
      tabla_futuros$Dif.PF[i] <- precio_ayer - precio_hoy
    } else {
      stop("La posición debe ser 'larga' o 'corta'.")
    }
    tabla_futuros$Liquidacion[i] <- tabla_futuros$Dif.PF[i] * valor_contrato
    
    margen_previo <- tabla_futuros$Margen[i-1]
    liquidacion_actual <- tabla_futuros$Liquidacion[i]
    margin_call_previo <- ifelse(tabla_futuros$Margin.Call[i-1] != 0 & tabla_futuros$Margin.Call[i-1] != "-", as.numeric(tabla_futuros$Margin.Call[i-1]), 0)
    
    margen_actual <- margen_previo + liquidacion_actual + margin_call_previo
    
    tabla_futuros$Margin.Call[i] <- 0
    
    if (margen_actual < margen_mantenimiento) {
      monto_margin_call <- margen_inicial - margen_actual
      tabla_futuros$Margin.Call[i] <- format(monto_margin_call, scientific = FALSE)
      tabla_futuros$Margen[i] <- margen_actual # Guardar el margen ANTES del call
    } else {
      tabla_futuros$Margen[i] <- margen_actual
    }
  }
  
  # Formatear la salida
  tabla_futuros <- tabla_futuros %>%
    mutate(
      Dif.PF = ifelse(T == tiempo[1], 0, as.character(Dif.PF)),
      Liquidacion = ifelse(T == tiempo[1], 0, as.character(format(Liquidacion, scientific = FALSE))),
      Margen = format(Margen, scientific = FALSE),
      Margin.Call = ifelse(Margin.Call != 0, Margin.Call, 0)
    )
  
  return(tabla_futuros)
}
nominal_contrato <- 1000
num_contratos <- 3
margen_inicial_total <- 0.063 * num_contratos * nominal_contrato
margen_mantenimiento_porcentaje <- 0.5
precio_referencia <- FT

#Simulación diaria del TRM y los Futuros
dias_totales <- 4 * 252 
dt_diario <- 1/252
set.seed(2032)
mb_trm_diario <- rep(NA, dias_totales)
mb_trm_diario[1] <- F0
for (t in 2:dias_totales) {
  mb_trm_diario[t] = mb_trm_diario[t-1] * exp((r_trm - (0.5 * (sigma_trm^2))) * dt_diario + sigma_trm * sqrt(dt_diario) * rnorm(1))
}
set.seed(2033)
mb_futures_diario <- rep(NA, dias_totales)
mb_futures_diario[1] <- FT
for (t in 2:dias_totales) {
  mb_futures_diario[t] = mb_futures_diario[t-1] * exp((r_trm - (0.5 * (sigma_trm^2))) * dt_diario + sigma_trm * sqrt(dt_diario) * rnorm(1))
}

#Función para generar la tabla de futuros
generar_tabla_futuros <- function(tiempo, precios_futuros, precio_futuro_inicial, valor_contrato, margen_inicial, margen_mantenimiento_porcentaje = 0.5, posicion = "larga") {
  
  margen_mantenimiento <- margen_inicial * margen_mantenimiento_porcentaje
  
  tabla_futuros <- data.frame(
    T = tiempo,
    Precio.Futuro = precios_futuros,
    Dif.PF = numeric(length(tiempo)),
    Liquidacion = numeric(length(tiempo)),
    Margen = numeric(length(tiempo)),
    Margin.Call = character(length(tiempo))
  )
  
  tabla_futuros$Margen[1] <- margen_inicial
  tabla_futuros$Dif.PF[1] <- 0
  tabla_futuros$Liquidacion[1] <- 0
  tabla_futuros$Margin.Call[1] <- 0
  
  for (i in 2:nrow(tabla_futuros)) {
    precio_hoy <- tabla_futuros$Precio.Futuro[i]
    precio_ayer <- tabla_futuros$Precio.Futuro[i-1]
    
    if (posicion == "larga") {
      tabla_futuros$Dif.PF[i] <- precio_hoy - precio_ayer
    } else if (posicion == "corta") {
      tabla_futuros$Dif.PF[i] <- precio_ayer - precio_hoy
    } else {
      stop("La posición debe ser 'larga' o 'corta'.")
    }
    tabla_futuros$Liquidacion[i] <- tabla_futuros$Dif.PF[i] * valor_contrato
    
    margen_previo <- tabla_futuros$Margen[i-1]
    liquidacion_actual <- tabla_futuros$Liquidacion[i]
    margin_call_previo <- ifelse(tabla_futuros$Margin.Call[i-1] != 0 & tabla_futuros$Margin.Call[i-1] != "-", as.numeric(tabla_futuros$Margin.Call[i-1]), 0)
    
    margen_actual <- margen_previo + liquidacion_actual + margin_call_previo
    
    tabla_futuros$Margin.Call[i] <- 0
    
    if (margen_actual < margen_mantenimiento) {
      monto_margin_call <- margen_inicial - margen_actual
      tabla_futuros$Margin.Call[i] <- format(monto_margin_call, scientific = FALSE)
      tabla_futuros$Margen[i] <- margen_actual # Guardar el margen ANTES del call
    } else {
      tabla_futuros$Margen[i] <- margen_actual
    }
  }
  
  return(tabla_futuros)
}

#Bucle de Rollover y Llamada a la Función
dias_por_periodo <- 6 * 21
resultados_por_periodo <- list()
for (i in 1:8) {
  inicio <- (i - 1) * dias_por_periodo + 1
  fin <- i * dias_por_periodo
  
  precios_periodo <- mb_futures_diario[inicio:fin]
  
  # Decisión de la posición
  if (precios_periodo[1] < precio_referencia) {
    posicion_actual <- "larga"
  } else {
    posicion_actual <- "corta"
  }
  
  tabla_periodo <- generar_tabla_futuros(
    tiempo = 1:dias_por_periodo,
    precios_futuros = precios_periodo,
    precio_futuro_inicial = precios_periodo[1],
    valor_contrato = nominal_contrato * num_contratos, # Valor total de la posición
    margen_inicial = margen_inicial_total,
    margen_mantenimiento_porcentaje = margen_mantenimiento_porcentaje,
    posicion = posicion_actual
  )
  
  resultados_por_periodo[[i]] <- tabla_periodo
}
calcular_flujo_caja_futuros <- function(tabla_futuros, margen_inicial_bloque, es_primera_tabla_serie = FALSE) {
  margin_call_numeric <- ifelse(tabla_futuros$Margin.Call == "0" | tabla_futuros$Margin.Call == "-" | is.na(tabla_futuros$Margin.Call),
                                0,
                                as.numeric(gsub(",", "", tabla_futuros$Margin.Call)))
  liquidacion_numeric <- as.numeric(gsub(",", "", tabla_futuros$Liquidacion))
  flujo_caja_por_periodo <- numeric(nrow(tabla_futuros))
  if (es_primera_tabla_serie) {
    flujo_caja_por_periodo[1] <- -margen_inicial_bloque
  } else {
    flujo_caja_por_periodo[1] <- 0
  }
  for (k in 2:(nrow(tabla_futuros) - 1)) {
    flujo_caja_por_periodo[k] <- liquidacion_numeric[k] - margin_call_numeric[k]
  }
  ultimo_periodo_idx <- nrow(tabla_futuros)
  flujo_caja_por_periodo[ultimo_periodo_idx] <-
    liquidacion_numeric[ultimo_periodo_idx] - margin_call_numeric[ultimo_periodo_idx]
  return(flujo_caja_por_periodo)
}
#Inicialización de vectores para acumular datos
flujos_cubiertos <- c()
periodos_cubiertos <- c()

#Bucle de Rollover y Acumulación de Datos
dias_por_periodo <- 6 * 21
num_periodos <- 8

for (i in 1:num_periodos) {
  inicio_dias <- (i - 1) * dias_por_periodo + 1
  fin_dias <- i * dias_por_periodo
  
  precios_periodo_diario <- mb_futures_diario[inicio_dias:fin_dias]
  
  # Decisión de la posición
  if (precios_periodo_diario[1] < precio_referencia) {
    posicion_actual <- "larga"
  } else {
    posicion_actual <- "corta"
  }
  
  # Generar la tabla de futuros
  tabla_margen <- generar_tabla_futuros(
    tiempo = 1:dias_por_periodo,
    precios_futuros = precios_periodo_diario,
    precio_futuro_inicial = precios_periodo_diario[1],
    valor_contrato = nominal_contrato * num_contratos,
    margen_inicial = margen_inicial_total,
    margen_mantenimiento_porcentaje = margen_mantenimiento_porcentaje,
    posicion = posicion_actual
  )
  
  # Calcular el flujo de caja del margen usando tu función
  es_primera <- (i == 1)
  flujo_caja_diario <- calcular_flujo_caja_futuros(
    tabla_futuros = tabla_margen,
    margen_inicial_bloque = margen_inicial_total,
    es_primera_tabla_serie = es_primera
  )
  
  # Obtener los períodos (meses del 73 al 120) para el flujo acumulado
    meses_del_periodo <- seq(from = 72 + (i-1)*6, to = 72 + i*6, by=1)
  periodos_diarios_acumulados <- rep(meses_del_periodo, each = 21, length.out = dias_por_periodo)

  # Acumular los datos en los vectores temporales
  flujos_cubiertos <- c(flujos_cubiertos, flujo_caja_diario)
  periodos_cubiertos <- c(periodos_cubiertos, periodos_diarios_acumulados)
}

#Consolidación y ordenamiento final
flujo_caja_cobertura_con_nombres <- tapply(flujos_cubiertos,
                                           INDEX = periodos_cubiertos,
                                           FUN = sum)

flujo_caja_cobertura_ordenado <- flujo_caja_cobertura_con_nombres[order(as.numeric(names(flujo_caja_cobertura_con_nombres)))]

print(flujo_caja_cobertura_ordenado)
##         72         73         74         75         76         77         78 
##  648606.70  539391.68  383569.03  394989.33  562882.70  434027.04 -511795.84 
##         79         80         81         82         83         84         85 
## -813968.09 -369354.09 -986051.65 -677853.92 -767308.37 -758222.73 -167055.65 
##         86         87         88         89         90         91         92 
## -500668.46 -591789.50 -673262.44 -690365.10 -627135.07 -523472.73 -234743.15 
##         93         94         95         96         97         98         99 
## -165190.43 -400876.98 -544746.95 -148881.23 -196720.14 -306053.73 -348255.28 
##        100        101        102        103        104        105        106 
## -251019.90 -168533.03 -271563.69  -14168.20 -205181.30 -217313.19 -339650.38 
##        107        108        109        110        111        112        113 
## -249545.18 -232931.51 -252271.30 -191418.38 -199792.92 -257837.54  -52551.75 
##        114        115        116        117        118        119 
## -172568.87 -215574.59 -169601.61  -77737.05 -156666.14 -207283.35
cuota_usd <- 1134.14

#Bucle principal para el análisis comparativo
dias_por_periodo <- 6 * 21
df_flujo_caja <- data.frame(
  Periodo_Cobertura = 1:8,
  Posicion = NA,
  Costo_Prestamo_Total = NA,
  Flujo_Caja_Margen = NA,
  Flujo_Caja_Neto_Total = NA
)
for (i in 1:8) {
  inicio_dias <- (i - 1) * dias_por_periodo + 1
  fin_dias <- i * dias_por_periodo
  
  precios_periodo_diario <- mb_futures_diario[inicio_dias:fin_dias]
  
  if (precios_periodo_diario[1] < precio_referencia) {
    posicion_actual <- "larga"
  } else {
    posicion_actual <- "corta"
  }
  
  tabla_margen <- generar_tabla_futuros(
    tiempo = 1:dias_por_periodo,
    precios_futuros = precios_periodo_diario,
    precio_futuro_inicial = precios_periodo_diario[1],
    valor_contrato = nominal_contrato * num_contratos,
    margen_inicial = margen_inicial_total,
    margen_mantenimiento_porcentaje = margen_mantenimiento_porcentaje,
    posicion = posicion_actual
  )
  
  trm_promedio_mensual <- mb_trm_diario[seq(inicio_dias, fin_dias, by = 21)]
  costo_prestamo_total <- sum(cuota_usd * trm_promedio_mensual)
  
  es_primera <- (i == 1)
  flujo_caja_futuros_diario <- calcular_flujo_caja_futuros(
    tabla_futuros = tabla_margen,
    margen_inicial_bloque = margen_inicial_total,
    es_primera_tabla_serie = es_primera
  )
  flujo_caja_margen <- sum(flujo_caja_futuros_diario)
  
  flujo_caja_neto_total <- flujo_caja_margen - costo_prestamo_total
  
  df_flujo_caja[i, "Posicion"] <- posicion_actual
  df_flujo_caja[i, "Costo_Prestamo_Total"] <- costo_prestamo_total
  df_flujo_caja[i, "Flujo_Caja_Margen"] <- flujo_caja_margen
  df_flujo_caja[i, "Flujo_Caja_Neto_Total"] <- flujo_caja_neto_total
}

print(df_flujo_caja)
##   Periodo_Cobertura Posicion Costo_Prestamo_Total Flujo_Caja_Margen
## 1                 1    corta             24933236         2963466.5
## 2                 2    larga             19831044        -4126332.0
## 3                 3    larga             15975657        -3381363.9
## 4                 4    larga             12537155        -2496165.3
## 5                 5    larga              9110904        -1419463.3
## 6                 6    larga              7161290        -1297421.9
## 7                 7    larga              5496779        -1186803.4
## 8                 8    larga              4487724         -999431.6
##   Flujo_Caja_Neto_Total
## 1             -21969770
## 2             -23957376
## 3             -19357021
## 4             -15033320
## 5             -10530367
## 6              -8458712
## 7              -6683583
## 8              -5487156
for (i in 1:8) {
  inicio_dias <- (i - 1) * dias_por_periodo + 1
  fin_dias <- i * dias_por_periodo
  
  precios_periodo_diario <- mb_futures_diario[inicio_dias:fin_dias]
# Código para definir la posición
  if (precios_periodo_diario[1] < precio_referencia) {
    posicion_actual <- "larga"
  } else {
    posicion_actual <- "corta"
  }
  df_flujo_caja[i, "Posicion"] <- posicion_actual
  
  tabla_margen <- generar_tabla_futuros(
    tiempo = 1:dias_por_periodo,
    precios_futuros = precios_periodo_diario,
    precio_futuro_inicial = precios_periodo_diario[1],
    valor_contrato = nominal_contrato * num_contratos,
    margen_inicial = margen_inicial_total,
    margen_mantenimiento_porcentaje = margen_mantenimiento_porcentaje,
    posicion = posicion_actual
  )
  
  ganancia_futuros_total <- sum(tabla_margen$Liquidacion)
  margin_calls_total <- sum(as.numeric(tabla_margen$Margin.Call[tabla_margen$Margin.Call != "0"]))
  
  df_flujo_caja[i, "Ganancia_Futuros_Total"] <- ganancia_futuros_total
  df_flujo_caja[i, "Margin_Calls_Total"] <- margin_calls_total
}


print(df_flujo_caja)
##   Periodo_Cobertura Posicion Costo_Prestamo_Total Flujo_Caja_Margen
## 1                 1    corta             24933236         2963466.5
## 2                 2    larga             19831044        -4126332.0
## 3                 3    larga             15975657        -3381363.9
## 4                 4    larga             12537155        -2496165.3
## 5                 5    larga              9110904        -1419463.3
## 6                 6    larga              7161290        -1297421.9
## 7                 7    larga              5496779        -1186803.4
## 8                 8    larga              4487724         -999431.6
##   Flujo_Caja_Neto_Total Ganancia_Futuros_Total Margin_Calls_Total
## 1             -21969770              2963655.5                0.0
## 2             -23957376             -2063166.0          2063166.0
## 3             -19357021             -1682550.6          1698813.3
## 4             -15033320             -1248082.7          1248082.7
## 5             -10530367              -708554.8           710908.5
## 6              -8458712              -648711.0           648711.0
## 7              -6683583              -593401.7           593401.7
## 8              -5487156              -499715.8           499715.8
library(ggplot2)
library(tidyr)
library(scales) 

df_long <- pivot_longer(
  df_flujo_caja,
  cols = c(Costo_Prestamo_Total, Flujo_Caja_Margen),
  names_to = "Tipo_Flujo",
  values_to = "Monto"
)

grafico_comparativo <- ggplot(df_long, aes(x = as.factor(Periodo_Cobertura), y = Monto, fill = Tipo_Flujo)) +
  geom_bar(stat = "identity", position = "dodge") +
  
   labs(
    title = "Comparación: Costo del Préstamo vs. Flujo de Caja de Futuros",
    subtitle = "Análisis de la efectividad de la cobertura por período",
    x = "Período de Cobertura (semestral)",
    y = "Monto (COP)",
    fill = "Tipo de Flujo"
  ) +
  
   scale_y_continuous(labels = dollar_format(prefix = "$", big.mark = ",", decimal.mark = ".")) +
  
   scale_fill_manual(
    values = c("Costo_Prestamo_Total" = "#E5635F", "Flujo_Caja_Margen" = "#3B7C57"),
    labels = c("Costo del Préstamo", "Flujo de Caja del Margen")
  ) +
  

  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "bottom"
  )

print(grafico_comparativo)

Conclusiones

Referencias

Banco de la República de Colombia. (2025, julio). Informe de política monetaria. https://www.banrep.gov.co/es/publicaciones-investigaciones/informe-politica-monetaria/julio-2025

Banco de la República de Colombia. (2025, agosto). Resultado de la Encuesta Mensual de Expectativas de Analistas Económicos (EME) - agosto 2025. https://www.banrep.gov.co/es/resultado-encuesta-mensual-expectativas-analistas-economicos-eme-agosto-2025

Banco de la República de Colombia. (s.f.). Tasa de cambio – TRM. Recuperado de https://www.banrep.gov.co/es/glosario/tasa-cambio-trm

Superintendencia Financiera de Colombia. (s.f.). Reportes PowerBI. https://www.superfinanciera.gov.co/powerbi/reportes/536/