1 Parte 1. Proceso del crédito

library(readxl); library(dplyr); library(tidyr); library(lubridate); library(ggplot2)

# --- Funciones robustas de lectura y limpieza
leer_trm_diaria <- function(ruta = "TRM_diaria.xlsx", hoja_preferida = c("TRM_diaria","Hoja1","Sheet1")){
  stopifnot(file.exists(ruta))
  shs <- readxl::excel_sheets(ruta)
  sheet <- if (length(intersect(hoja_preferida, shs))>0) intersect(hoja_preferida, shs)[1] else shs[1]
  # intentos con distintos 'skip'
  try_read <- function(skip){
    df <- readxl::read_excel(ruta, sheet = sheet, skip = skip)
    names(df) <- make.names(names(df), unique = TRUE)
    df
  }
  candidatos_skip <- c(0, 1, 2, 3, 4)
  for (sk in candidatos_skip){
    df <- try_read(sk)
    nms <- tolower(names(df))
    # buscamos columnas tipo fecha y valor
    id_fecha <- which(grepl("date|fecha", nms))[1]
    id_val   <- which(grepl("trm|valor|price|close|usd", nms))[1]
    if (!is.na(id_fecha) && !is.na(id_val)){
      df <- df[, c(id_fecha, id_val)]
      names(df) <- c("date","TRM")
      # conversión de fecha (excel serial o Date)
      if (!inherits(df$date, "Date")) df$date <- as.Date(as.numeric(df$date), origin = "1899-12-30")
      df$TRM <- suppressWarnings(as.numeric(df$TRM))
      df <- df %>% filter(!is.na(date), !is.na(TRM)) %>% arrange(date)
      if (nrow(df) > 200) return(df)
    }
  }
  stop("No se pudo detectar columnas 'date' y 'TRM' en ", ruta)
}

trm <- leer_trm_diaria()
summary(trm$TRM)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1748    1935    3016    3019    3856    5061

1.0.1 2.1 Retornos diarios y mensualización

# Retornos diarios
trm_d <- trm %>% arrange(date) %>%
  mutate(ret_d = TRM/lag(TRM) - 1,
         ret_log_d = log(TRM/lag(TRM)))

vol_d  <- sd(trm_d$ret_d, na.rm = TRUE)
vol_m  <- vol_d * sqrt(21)
vol_a  <- vol_d * sqrt(252)

data.frame(
  media_diaria = mean(trm_d$ret_d, na.rm = TRUE),
  desv_diaria  = vol_d,
  vol_mensual_imp = vol_m,
  vol_anual_imp   = vol_a
)
##   media_diaria desv_diaria vol_mensual_imp vol_anual_imp
## 1 0.0001311802 0.006166817      0.02825991    0.09789519

1.0.2 2.2 Cierres de fin de mes (EoM) y estadísticos mensuales

trm_eom <- trm %>%
  group_by(yy = year(date), mm = month(date)) %>%
  slice_max(order_by = date, n = 1, with_ties = FALSE) %>%
  ungroup() %>%
  transmute(fecha = date, TRM)

trm_eom <- trm_eom %>% arrange(fecha) %>%
  mutate(ret_m = TRM/lag(TRM) - 1,
         ret_log_m = log(TRM/lag(TRM)))

stats_m_trm <- data.frame(
  media_mensual = mean(trm_eom$ret_m, na.rm = TRUE),
  desv_mensual  = sd(trm_eom$ret_m,   na.rm = TRUE)
)
stats_m_trm
##   media_mensual desv_mensual
## 1  0.0001311802  0.006166817

1.0.3 2.3 Gráfico histórico de TRM

ggplot(trm, aes(date, TRM)) +
  geom_line() +
  labs(title = "TRM histórica (COP/USD)", x = "Fecha", y = "TRM")

1.1 3. Simulación BMG (GBM) mensual de la TRM (120 meses)

set.seed(123)
M  <- 3000
Tn <- 120
S0 <- tail(trm$TRM, 1)
mu_m  <- mean(trm_eom$ret_m, na.rm = TRUE)
sig_m <- sd(trm_eom$ret_m,   na.rm = TRUE)

Z   <- matrix(rnorm(M*Tn), nrow = M)
dr  <- (mu_m - 0.5*sig_m^2)
df  <- sig_m

TRM_mc <- matrix(NA_real_, nrow = M, ncol = Tn + 1)
TRM_mc[,1] <- S0
for (t in 1:Tn) {
  TRM_mc[,t+1] <- TRM_mc[,t] * exp(dr + df * Z[,t])
}
TRM_mc <- TRM_mc[, -1]  # M x 120

# Fan chart
pcts <- c(0.05,0.25,0.50,0.75,0.95)
fan <- sapply(1:Tn, function(j) quantile(TRM_mc[,j], probs = pcts, na.rm = TRUE))
fan_df <- as.data.frame(t(fan)); colnames(fan_df) <- paste0("p", pcts*100); fan_df$Mes <- 1:Tn

ggplot() +
  geom_ribbon(data = fan_df, aes(x = Mes, ymin = p5, ymax = p95), alpha = 0.15) +
  geom_ribbon(data = fan_df, aes(x = Mes, ymin = p25, ymax = p75), alpha = 0.25) +
  geom_line(data = fan_df, aes(x = Mes, y = p50))

1.2 4. Crédito en USD (sistema francés) y cuota en COP

capex_usd <- 75000; down_pct <- 0.15; apr <- 0.085; years <- 10
n <- years*12; r_m <- apr/12
principal_usd <- capex_usd * (1 - down_pct)
cuota_usd <- principal_usd * r_m / (1 - (1 + r_m)^(-n))

# Cuota en COP simulada
cuota_cop_mc <- TRM_mc * cuota_usd  # M x 120

data.frame(
  principal_usd = principal_usd,
  tasa_mensual  = r_m,
  cuota_usd     = cuota_usd
)
##   principal_usd tasa_mensual cuota_usd
## 1         63750  0.007083333  790.4088

2 Parte 2. Proceso de futuro (BVC)

2.1 1. Histórico del futuro: retornos y desviación mensual

leer_futuro <- function(ruta = "TRM FUTURO.xlsx"){
  stopifnot(file.exists(ruta))
  shs <- readxl::excel_sheets(ruta)
  df <- readxl::read_excel(ruta, sheet = shs[1])
  nms <- tolower(names(df))
  id_fecha <- which(grepl("date|fecha", nms))[1]
  id_p     <- which(grepl("fut|precio|price|trm|close", nms))[1]
  if (is.na(id_fecha) || is.na(id_p)) stop("No encuentro columnas fecha/precio en ", ruta)
  df <- df[, c(id_fecha, id_p)]; names(df) <- c("fecha","Futuro")
  if (!inherits(df$fecha,"Date")) df$fecha <- as.Date(as.numeric(df$fecha), origin = "1899-12-30")
  df$Futuro <- suppressWarnings(as.numeric(df$Futuro))
  df <- df %>% filter(!is.na(fecha), !is.na(Futuro)) %>% arrange(fecha)
  df
}

fut_raw <- leer_futuro()

fut_eom <- fut_raw %>%
  group_by(y=year(fecha), m=month(fecha)) %>%
  slice_max(order_by = fecha, n = 1, with_ties = FALSE) %>%
  ungroup() %>%
  transmute(fecha, Futuro)

fut_eom <- fut_eom %>% arrange(fecha) %>%
  mutate(ret_m = Futuro/lag(Futuro) - 1,
         ret_log_m = log(Futuro/lag(Futuro)))

stats_m_fut <- data.frame(
  media_mensual = mean(fut_eom$ret_m, na.rm = TRUE),
  desv_mensual  = sd(fut_eom$ret_m,   na.rm = TRUE)
)
stats_m_fut
##   media_mensual desv_mensual
## 1   0.004276231   0.03719266

2.2 2. Precio inicial del futuro (F₀) y simulación (meses 61–120)

# F0 por trayectoria usando S_60 simulado
rd <- 0.0873; rf <- 0.0436; Tm <- 1/12
S_start <- TRM_mc[,60]                # TRM en mes 60 (inicio cobertura)
F0_vec  <- S_start * exp((rd - rf) * Tm)

# Simulación del futuro 60 meses (años 6–10) con parámetros del futuro
set.seed(123)
Thedge <- 60
mu_f  <- mean(fut_eom$ret_m, na.rm = TRUE)
sig_f <- sd(fut_eom$ret_m,   na.rm = TRUE)
Zf <- matrix(rnorm(nrow(TRM_mc)*Thedge), nrow = nrow(TRM_mc))
drf <- (mu_f - 0.5*sig_f^2); dff <- sig_f

F_mc <- matrix(NA_real_, nrow = nrow(TRM_mc), ncol = Thedge + 1)
F_mc[,1] <- F0_vec
for (t in 1:Thedge) {
  F_mc[,t+1] <- F_mc[,t] * exp(drf + dff * Zf[,t])
}
F_mc <- F_mc[ , -1]  # M x 60

# Fan chart
pcts <- c(0.05,0.25,0.50,0.75,0.95)
fanF <- sapply(1:Thedge, function(j) quantile(F_mc[,j], probs = pcts, na.rm = TRUE))
fanF_df <- as.data.frame(t(fanF)); colnames(fanF_df) <- paste0("p", pcts*100); fanF_df$Mes <- 61:120

ggplot() +
  geom_ribbon(data = fanF_df, aes(x = Mes, ymin = p5, ymax = p95), alpha = 0.15) +
  geom_ribbon(data = fanF_df, aes(x = Mes, ymin = p25, ymax = p75), alpha = 0.25) +
  geom_line(data = fanF_df, aes(x = Mes, y = p50))

2.3 3. Exposición, márgenes y flujo de caja de margen

tam_contrato_usd <- 50000
capex_cop <- 300e6
cover_pct <- 0.75

expo_cop <- cover_pct * capex_cop
expo_usd <- expo_cop / mean(S_start)
num_ctos <- max(1, round(expo_usd / tam_contrato_usd))

IM_pct <- 0.063; MM_ratio <- 0.75
IM_cto <- IM_pct * (mean(F0_vec) * tam_contrato_usd)
MM_cto <- MM_ratio * IM_cto
IM_tot <- IM_cto * num_ctos
MM_tot <- MM_cto * num_ctos

# P&L por diferencias
PnL_fut <- matrix(0, nrow = nrow(F_mc), ncol = ncol(F_mc))
for (t in 2:ncol(F_mc)){
  dF <- F_mc[,t] - F_mc[,t-1]
  PnL_fut[,t] <- dF * tam_contrato_usd * num_ctos   # posición LONG por defecto
}

# Cuenta de margen
flow_margen <- matrix(0, nrow = nrow(F_mc), ncol = ncol(F_mc))
balance     <- matrix(NA_real_, nrow = nrow(F_mc), ncol = ncol(F_mc))
balance[,1] <- IM_tot

for (t in 2:ncol(F_mc)){
  balance[,t] <- balance[,t-1] + PnL_fut[,t]
  need_topup <- balance[,t] < MM_tot
  if (any(need_topup)){
    topup <- IM_tot - balance[need_topup, t]
    balance[need_topup, t] <- IM_tot
    flow_margen[need_topup, t] <- flow_margen[need_topup, t] + topup
  }
  excess <- balance[,t] - IM_tot
  need_withdraw <- excess > 0
  if (any(need_withdraw)){
    retiro <- excess[need_withdraw]
    balance[need_withdraw, t] <- IM_tot
    flow_margen[need_withdraw, t] <- flow_margen[need_withdraw, t] - retiro
  }
}
avg_margin_cash <- mean(rowSums(flow_margen), na.rm = TRUE)
data.frame(num_contratos = num_ctos, IM_total = IM_tot, MM_total = MM_tot, caja_prom_margen = round(avg_margin_cash))
##   num_contratos IM_total MM_total caja_prom_margen
## 1             1 12415896  9311922        -61875821

2.4 4. Cobertura: pago del crédito vs. futuro (meses 61–120)

# Pagos de crédito en COP (61–120)
pay_cop_hedge <- cuota_cop_mc[,61:120, drop = FALSE]  # M x 60
neto_cubierto <- pay_cop_hedge - PnL_fut              # M x 60

# Efectividad (reducción de volatilidad)
vol_sin <- apply(pay_cop_hedge, 2, sd, na.rm = TRUE)
vol_con <- apply(neto_cubierto,  2, sd, na.rm = TRUE)
reducc  <- 1 - (vol_con/vol_sin)
data.frame(reduccion_vol_media_pct = round(100*mean(reducc, na.rm = TRUE), 2))
##   reduccion_vol_media_pct
## 1                -4624.78

2.4.1 4.1 Fan chart: cuota en COP sin/ con cobertura

pcts <- c(0.05,0.25,0.50,0.75,0.95)
pay_pctl <- sapply(1:60, function(j) quantile(pay_cop_hedge[,j], probs = pcts, na.rm = TRUE))
net_pctl <- sapply(1:60, function(j) quantile(neto_cubierto[,j],  probs = pcts, na.rm = TRUE))

fan_pay <- as.data.frame(t(pay_pctl)); colnames(fan_pay) <- paste0("p", pcts*100); fan_pay$Mes <- 61:120
fan_net <- as.data.frame(t(net_pctl)); colnames(fan_net) <- paste0("p", pcts*100); fan_net$Mes <- 61:120

ggplot() +
  geom_ribbon(data = fan_pay, aes(x = Mes, ymin = p5,  ymax = p95), alpha = 0.12) +
  geom_ribbon(data = fan_net, aes(x = Mes, ymin = p5, ymax = p95), alpha = 0.12) +
  geom_line(data = fan_pay, aes(x = Mes, y = p50)) +
  geom_line(data = fan_net, aes(x = Mes, y = p50))

2.5 1. Análisis fundamental de la TRM

  • Resumen de factores macro relevantes (inflación, diferencial de tasas, cuenta corriente, riesgo país, flujos de portafolio).

  • Expectativas a 12 meses: referenciar proyecciones de BanRep, FMI, y bancos de inversión.

  • Implicaciones para una empresa con pasivo en USD: riesgo de conversión a COP y sensibilidad de caja.

  • Parte 1. Crédito en dólares ###

  • Monto inicial requerido (300 millones COP): dato del enunciado.

  • Conversión a USD (TRM inicial ≈ 4000 COP/USD): suponemos una TRM inicial de 4.000 COP/USD.

  • Fuente: decisión de modelado (para simplificar, porque la TRM varía día a día).

  • Pago inicial (15%): indicado en el enunciado.

  • Tasa de interés en EE. UU. y plazo (10 años, sistema francés): tomados como condiciones del crédito internacional, elegidos de un banco de EE. UU. (ejemplo: tasas promedio de préstamos corporativos de la FED o un banco como JPMorgan/Bank of America).

  • Supuesto práctico: usamos un valor referencial para poder calcular la tabla de amortización.)*

2.6 2. TRM histórica: lectura, limpieza y estadísticas

  • TRM diaria (histórica): hoja TRM_diaria en tu archivo.

  • Descargada directamente de la Superintendencia Financiera de Colombia (SFC) o la BVC (Bolsa de Valores de Colombia), que publican la TRM oficial cada día.

  • Retornos y desviación estándar mensuales:

  • Calculados a partir de esa serie diaria (usando variaciones logarítmicas o simples, agrupadas por mes).

#Simulación GBM

  • Parámetros:

  • μ mensual ≈ 0,003589 (0,3589%) y

  • σ mensual ≈ 0,036867 (3,6867%)

  • Derivados de la serie histórica de la TRM (con los cálculos de media y desviación estándar mensuales).

  • Método: Browniano Geométrico para proyectar escenarios futuros de TRM

#Parte 3. Futuros TRM (BVC)

  • Subyacente: contrato futuro de TRM listado en la BVC (Bolsa de Valores de Colombia), por ejemplo el TRMM25F (TRM mensual con vencimiento a febrero de 2025).

  • Tamaño del contrato:

  • Estándar TRM = 50.000 USD.

  • Fuente: especificaciones oficiales de productos derivados en la BVC.

  • Margen inicial (6,3%) y margen de mantenimiento (75% del inicial):

  • Fuente: manual de la BVC – Derivados de Divisas (condiciones vigentes para futuros TRM).

  • Precios históricos del futuro (TRMM25F_Futuro(mes)):

  • Extraídos de información de la BVC (informes de precios de futuros).

  • se organizó de forma mensual para luego calcular retornos y σ.

  • Simulación GBM de futuros

  • Igual que con la TRM spot: partiendo de media y desviación estándar mensuales calculadas con la serie histórica del futuro TRM.

  • Primer mes (mes 61) se arranca con el valor teórico del futuro:

  • donde S = TRM spot del mes anterior, r_d = tasa de interés doméstica (Colombia), r_f = tasa de EE. UU., y T = 1/12 (un mes).

#Parte 4. Márgenes y PnL

  • PnL mensual de futuros:

  • con los precios simulados 𝐹𝑡

  • Saldo de márgenes, margin calls:

  • Fórmulas aplicadas mes a mes usando los parámetros oficiales de la BVC (6,3% inicial, 75% mantenimiento).

#Parte 5. Flujo del crédito vs cobertura

  • Flujo sin cobertura: cuotas USD (del crédito) × TRM simulada (Hoja Sim. TRM).

  • Flujo cubierto: cuotas USD × TRM simulada + PnL de futuros (Hoja5).

3 Conclusiones

  • Efectividad de la cobertura: la reducción de volatilidad promedio sobre meses 61–120 es reportada en el bloque anterior. Interprétala como el beneficio de riesgo.
  • Coste de liquidez: los depósitos netos por margen (caja_prom_margen) capturan la presión de caja del hedge.
  • Riesgo de base: con datos mensuales, la correlación Futuro–TRM es alta.