1 Enunciado del caso

1.1 Contexto

El precio del dólar estadounidense frente al peso colombiano es uno de los indicadores más observados de la economía nacional. El USD/COP de contado funciona como termómetro de los flujos internacionales: cuando el par sube, el peso se debilita, lo que suele asociarse a salida de dólares o a mayor demanda por la divisa; cuando baja, ocurre lo contrario.

El propósito del estudio del que hace parte esta base es estimar un rango de negociación, es decir, un soporte y una resistencia para el USD/COP en un horizonte determinado. No se busca pronosticar la dirección del movimiento, sino acotar su magnitud.

1.2 Pregunta que guía este caso

De ese propósito se desprende la pregunta que organiza el análisis descriptivo:

¿El comportamiento del USD/COP es homogéneo a lo largo del tiempo y entre estados del mercado, o existen grupos de días con perfiles estadísticos claramente distintos?

La respuesta importa de manera directa: si los indicadores difieren entre grupos, entonces un rango de amplitud fija sería inadecuado y el ancho de la banda tendría que depender del estado del mercado.

1.3 Tareas desarrolladas

  1. Construcción de tablas de frecuencia absoluta y relativa, e identificación de la moda, para las variables cualitativas.
  2. Cálculo de indicadores de tendencia central, posición y dispersión para las variables cuantitativas, seleccionando los apropiados según la distribución observada.
  3. Comparación de los indicadores entre grupos: por estado del mercado y por periodo.
  4. Interpretación de los resultados frente al propósito del estudio.
  5. Informe descriptivo con los patrones principales.

Advertencia metodológica. Las variables cualitativas de este caso se construyen categorizando información de la propia base, según criterios que se explicitan más adelante. El análisis es exploratorio y descriptivo: describe asociaciones observadas y no permite extraer conclusiones causales.

2 Base de datos

panel <- readRDS(file.path(RAIZ, "data_clean", "panel_limpio.rds"))

datos <- data.frame(
  fecha       = index(panel),
  spot        = as.numeric(panel$usdcop_spot),
  retorno     = as.numeric(panel$r_usdcop_spot) * 100,
  volatilidad = as.numeric(panel$vol_realizada_21d),
  carry       = as.numeric(panel$carry_politica),
  vix         = as.numeric(panel$vix),
  cds         = as.numeric(panel$cds_colombia_5y)
)

cat("Observaciones:", nrow(datos), "| Periodo:",
    format(min(datos$fecha)), "a", format(max(datos$fecha)), "\n")
## Observaciones: 3008 | Periodo: 2015-01-02 a 2026-07-31

La base documentada en la Actividad 1.1.2 contiene 3.008 días de negociación y 46 variables, construidas a partir de LSEG Workspace y del Banco de la República. Para este caso se trabaja con un subconjunto de variables directamente ligadas a la pregunta.

datatable(head(datos, 60), rownames = FALSE,
          options = list(pageLength = 8, dom = "tip", scrollX = TRUE),
          caption = "Vista de la base de trabajo") |>
  formatRound(c("spot","retorno","volatilidad","carry","vix","cds"), 2)

2.1 Variables empleadas

Variables utilizadas en el caso
Variable Descripcion Naturaleza Escala
retorno Retorno logarítmico diario del USD/COP, en % Cuantitativa Razón
volatilidad Volatilidad realizada a 21 días, anualizada en % Cuantitativa Razón
carry Diferencial de tasas de política, BanRep menos Fed, en puntos porcentuales Cuantitativa Razón
regimen Estado del mercado según tensión global y local Cualitativa Ordinal
periodo Etapa del ciclo macroeconómico Cualitativa Ordinal
direccion Signo del movimiento diario del peso Cualitativa Nominal

3 Construcción de las variables de agrupación

# --- Régimen de mercado (ordinal) --------------------------------------------
q75_vix <- quantile(datos$vix, 0.75, na.rm = TRUE)
q75_cds <- quantile(datos$cds, 0.75, na.rm = TRUE)

datos$regimen <- with(datos, ifelse(
  is.na(vix) | is.na(cds), NA,
  ifelse(vix > q75_vix, "Aversión al riesgo",
  ifelse(cds > q75_cds, "Estrés local", "Normal"))))
datos$regimen <- factor(datos$regimen,
  levels = c("Normal", "Estrés local", "Aversión al riesgo"), ordered = TRUE)

# --- Periodo (ordinal) --------------------------------------------------------
anio <- as.integer(format(datos$fecha, "%Y"))
datos$periodo <- ifelse(anio <= 2019, "2015-2019 Pre-pandemia",
                 ifelse(anio <= 2021, "2020-2021 Pandemia",
                                      "2022-2026 Ciclo de alzas"))
datos$periodo <- factor(datos$periodo,
  levels = c("2015-2019 Pre-pandemia", "2020-2021 Pandemia",
             "2022-2026 Ciclo de alzas"), ordered = TRUE)

# --- Dirección diaria del peso (nominal) --------------------------------------
datos$direccion <- with(datos, ifelse(
  is.na(retorno), NA,
  ifelse(retorno > 0, "Depreciación del peso",
  ifelse(retorno < 0, "Apreciación del peso", "Sin cambio"))))
datos$direccion <- factor(datos$direccion,
  levels = c("Apreciación del peso", "Sin cambio", "Depreciación del peso"))

cat("Umbrales:  VIX >", round(q75_vix, 2), " |  CDS >", round(q75_cds, 1), "pb\n")
## Umbrales:  VIX > 21.14  |  CDS > 212.2 pb

Criterio del régimen. Un día se clasifica como aversión al riesgo cuando el VIX supera su tercer cuartil histórico, y como estrés local cuando quien lo supera es el CDS de Colombia. La evaluación es jerárquica: la tensión global se examina primero, de modo que la categoría de estrés local recoge los días de tensión propia del país sin tensión global acompañante.

Criterio del periodo. Se separan tres etapas macroeconómicas: los años previos a la pandemia, el choque de 2020 y 2021, y el ciclo de alzas de tasas posterior.

4 Tarea 1. Variables cualitativas: frecuencias y moda

tabla_frecuencia <- function(x, etiqueta) {
  x  <- x[!is.na(x)]
  ni <- table(x); fi <- prop.table(ni)
  out <- data.frame(names(ni), as.integer(ni), round(as.numeric(fi), 4),
                    round(as.numeric(fi)*100, 2), cumsum(as.integer(ni)),
                    round(cumsum(as.numeric(fi))*100, 2))
  names(out) <- c(etiqueta, "$n_i$", "$f_i$", "$\\%$", "$N_i$", "$F_i\\ \\%$")
  out
}
moda_cual <- function(x) names(which.max(table(x)))

4.1 Régimen de mercado

kable(tabla_frecuencia(datos$regimen, "Régimen de mercado"),
      caption = "Frecuencias — régimen de mercado", align = "lrrrrr")
Frecuencias — régimen de mercado
Régimen de mercado \(n_i\) \(f_i\) \(\%\) \(N_i\) \(F_i\ \%\)
Normal 1850 0.6223 62.23 1850 62.23
Estrés local 375 0.1261 12.61 2225 74.84
Aversión al riesgo 748 0.2516 25.16 2973 100.00
cat("Moda:", moda_cual(datos$regimen), "\n")
## Moda: Normal

4.2 Periodo

kable(tabla_frecuencia(datos$periodo, "Periodo"),
      caption = "Frecuencias — periodo", align = "lrrrrr")
Frecuencias — periodo
Periodo \(n_i\) \(f_i\) \(\%\) \(N_i\) \(F_i\ \%\)
2015-2019 Pre-pandemia 1299 0.4318 43.18 1299 43.18
2020-2021 Pandemia 518 0.1722 17.22 1817 60.41
2022-2026 Ciclo de alzas 1191 0.3959 39.59 3008 100.00
cat("Moda:", moda_cual(datos$periodo), "\n")
## Moda: 2015-2019 Pre-pandemia

4.3 Dirección diaria del peso

kable(tabla_frecuencia(datos$direccion, "Dirección diaria"),
      caption = "Frecuencias — dirección diaria del peso", align = "lrrrrr")
Frecuencias — dirección diaria del peso
Dirección diaria \(n_i\) \(f_i\) \(\%\) \(N_i\) \(F_i\ \%\)
Apreciación del peso 1501 0.4992 49.92 1501 49.92
Sin cambio 39 0.0130 1.30 1540 51.21
Depreciación del peso 1467 0.4879 48.79 3007 100.00
cat("Moda:", moda_cual(datos$direccion), "\n")
## Moda: Apreciación del peso
par(mfrow = c(1, 3), mar = c(6, 4, 3, 1))
barplot(table(datos$regimen), col = c(VERDE, ORO, ROJO), border = NA, las = 2,
        main = "Régimen", ylab = "Días", cex.names = .75)
barplot(table(datos$periodo), col = AZUL, border = NA, las = 2,
        main = "Periodo", cex.names = .75)
barplot(table(datos$direccion), col = c(VERDE, GRIS, ROJO), border = NA, las = 2,
        main = "Dirección", cex.names = .75)

par(mfrow = c(1, 1))

Lectura. La moda del régimen es Normal, con cerca de dos tercios de las jornadas. La del periodo corresponde a la etapa previa a la pandemia, simplemente porque es la más extensa. En la dirección diaria la moda es apreciación del peso, pero con una ventaja mínima sobre la depreciación: el reparto es prácticamente equilibrado.

5 Tarea 2. Variables cuantitativas: elección de indicadores

Antes de comparar entre grupos conviene examinar la forma de cada distribución, porque de ella depende qué indicadores son apropiados.

vars <- c("retorno", "volatilidad", "carry")
forma <- t(sapply(vars, function(v) {
  x <- datos[[v]]; x <- x[!is.na(x)]
  c(Media = mean(x), Mediana = median(x), `Desv. est.` = sd(x),
    Asimetría = psych::skew(x), `Curtosis (exceso)` = psych::kurtosi(x))
}))
kable(round(forma, 3), caption = "Forma de las distribuciones")
Forma de las distribuciones
Media Mediana Desv. est. Asimetría Curtosis (exceso)
retorno 0.009 0.000 0.869 0.482 7.044
volatilidad 12.751 11.734 5.198 3.011 18.704
carry 4.695 4.920 2.135 0.056 -1.292
par(mfrow = c(1, 3), mar = c(4, 4, 3, 1))
hist(datos$retorno, breaks = 40, col = CLARO, border = "white",
     main = "Retorno diario (%)", xlab = "")
hist(datos$volatilidad, breaks = 40, col = CLARO, border = "white",
     main = "Volatilidad 21d (%)", xlab = "")
hist(datos$carry, breaks = 40, col = CLARO, border = "white",
     main = "Carry de política (pp)", xlab = "")

par(mfrow = c(1, 1))

Decisión sobre los indicadores.

  • El retorno es aproximadamente simétrico (asimetría cercana a cero) pero con curtosis muy elevada. La media y la desviación estándar son representativas del centro y de la escala, pero deben acompañarse de indicadores de cola, porque la desviación estándar por sí sola subestima la frecuencia de los movimientos extremos.
  • La volatilidad presenta una fuerte asimetría positiva. En este caso la media queda arrastrada por la cola derecha, de manera que la mediana y el rango intercuartílico son descriptores más fieles del comportamiento típico; la media se conserva únicamente para evidenciar el efecto de los extremos.
  • El carry no es unimodal en sentido estricto: refleja decisiones de política monetaria que se mantienen durante meses, por lo que su distribución responde más a la duración de cada etapa que a un proceso aleatorio. Para él resultan más informativos la mediana y los cuartiles.
indic <- t(sapply(vars, function(v) {
  x <- datos[[v]]; x <- x[!is.na(x)]
  cv <- 100 * sd(x) / abs(mean(x))
  # El coeficiente de variación solo es interpretable si la media se aleja de cero.
  if (abs(mean(x)) < 0.1 * sd(x)) cv <- NA
  c(n = length(x), Mínimo = min(x), Q1 = unname(quantile(x, .25)), Mediana = median(x),
    Q3 = unname(quantile(x, .75)), Máximo = max(x), Media = mean(x),
    `Desv. est.` = sd(x), RIC = IQR(x), `CV %` = cv)
}))
kable(round(indic, 3), caption = "Indicadores de posición, tendencia central y dispersión")
Indicadores de posición, tendencia central y dispersión
n Mínimo Q1 Mediana Q3 Máximo Media Desv. est. RIC CV %
retorno 3007 -6.967 -0.468 0.000 0.464 9.048 0.009 0.869 0.932 NA
volatilidad 2987 4.468 9.511 11.734 15.246 53.842 12.751 5.198 5.735 40.768
carry 3008 1.650 2.550 4.920 6.630 8.420 4.695 2.135 4.080 45.474

Nota sobre el coeficiente de variación. Este indicador expresa la dispersión como proporción de la media, de modo que pierde sentido cuando la media se acerca a cero: el cociente se dispara sin que ello refleje mayor variabilidad. Es exactamente lo que ocurre con el retorno diario, cuya media es de 0,009 % frente a una desviación estándar de 0,869 %. Por esa razón se reporta como no disponible en esa fila y la dispersión se lee mediante la desviación estándar y el rango intercuartílico. En la volatilidad y en el carry, cuyas medias están lejos de cero, el coeficiente sí es informativo.

6 Tarea 3. Comparación de indicadores entre grupos

Esta es la tarea central del caso. Se contrastan los indicadores de las variables cuantitativas entre las categorías de las dos variables de agrupación.

6.1 Comparación por régimen de mercado

db <- describeBy(datos[, c("retorno","volatilidad","carry")],
                 group = datos$regimen, mat = TRUE, digits = 3)
tab <- db[, c("group1","n","mean","median","sd","min","max","skew","kurtosis")]
names(tab) <- c("Régimen","n","Media","Mediana","Desv. est.","Mínimo","Máximo",
                "Asimetría","Curtosis")
tab$Variable <- sub("[0-9]+$", "", rownames(db))
kable(tab[, c("Variable", names(tab)[1:9])], row.names = FALSE,
      caption = "Indicadores por régimen de mercado (psych::describeBy)")
Indicadores por régimen de mercado (psych::describeBy)
Variable Régimen n Media Mediana Desv. est. Mínimo Máximo Asimetría Curtosis
retorno Normal 1849 0.008 0.000 0.748 -3.183 3.332 0.062 1.308
retorno Estrés local 375 -0.095 -0.147 0.954 -2.813 3.285 0.376 0.744
retorno Aversión al riesgo 748 0.067 0.027 1.065 -6.967 9.048 0.862 10.705
volatilidad Normal 1831 11.382 10.918 3.474 4.618 24.014 0.621 0.095
volatilidad Estrés local 375 14.669 14.982 3.645 5.491 21.730 -0.206 -0.944
volatilidad Aversión al riesgo 746 14.891 12.901 7.660 4.468 53.842 2.928 11.230
carry Normal 1850 4.470 4.380 2.072 1.650 8.380 0.169 -1.276
carry Estrés local 375 6.801 7.170 1.340 2.920 8.420 -0.437 -1.230
carry Aversión al riesgo 748 4.117 3.920 1.978 1.650 8.180 0.301 -1.148
par(mfrow = c(1, 2), mar = c(7, 4, 3, 1))
boxplot(retorno ~ regimen, data = datos, col = c(VERDE, ORO, ROJO), las = 2,
        main = "Retorno diario por régimen", ylab = "%", xlab = "", cex.axis = .75)
boxplot(volatilidad ~ regimen, data = datos, col = c(VERDE, ORO, ROJO), las = 2,
        main = "Volatilidad por régimen", ylab = "%", xlab = "", cex.axis = .75)

par(mfrow = c(1, 1))

Interpretación. El contraste entre grupos revela el patrón más importante de todo el caso.

La media del retorno se mantiene cerca de cero en los tres regímenes, de modo que el estado del mercado no anticipa la dirección del movimiento. Sin embargo, la desviación estándar crece de forma sostenida al pasar de un régimen a otro: alrededor de 0,75 % en días normales, 0,95 % en estrés local y 1,07 % en aversión al riesgo. Es decir, el régimen no cambia hacia dónde se mueve el precio, pero sí cuánto se mueve.

La curtosis refuerza la lectura: en los días de aversión al riesgo alcanza un valor extraordinariamente alto, muy por encima del que se observa en los demás regímenes. Los episodios verdaderamente extremos se concentran en ese grupo.

En la volatilidad realizada la diferencia es de nivel: los regímenes de tensión superan claramente al régimen normal. Conviene notar que en aversión al riesgo la media supera con holgura a la mediana, señal de que ese grupo contiene los valores más extremos de toda la muestra.

El carry de política también distingue los regímenes, aunque por una razón de naturaleza distinta. Su mediana alcanza 7,17 puntos porcentuales en los días de estrés local, frente a 4,38 en los días normales y 3,92 en aversión al riesgo. No refleja una reacción del mercado sino la coincidencia con etapas de tasas de intervención elevadas: los episodios de tensión propia de Colombia se concentran en periodos de política monetaria restrictiva. Este contraste es útil porque muestra que las dos agrupaciones del caso no son redundantes: el régimen captura tensión financiera y el periodo captura la etapa del ciclo, y ambas dimensiones se cruzan.

6.2 Comparación por periodo

db2 <- describeBy(datos[, c("retorno","volatilidad","carry")],
                  group = datos$periodo, mat = TRUE, digits = 3)
tab2 <- db2[, c("group1","n","mean","median","sd","min","max","skew","kurtosis")]
names(tab2) <- c("Periodo","n","Media","Mediana","Desv. est.","Mínimo","Máximo",
                 "Asimetría","Curtosis")
tab2$Variable <- sub("[0-9]+$", "", rownames(db2))
kable(tab2[, c("Variable", names(tab2)[1:9])], row.names = FALSE,
      caption = "Indicadores por periodo (psych::describeBy)")
Indicadores por periodo (psych::describeBy)
Variable Periodo n Media Mediana Desv. est. Mínimo Máximo Asimetría Curtosis
retorno 2015-2019 Pre-pandemia 1298 0.025 0.011 0.858 -3.439 3.332 0.027 1.211
retorno 2020-2021 Pandemia 518 0.041 0.026 1.004 -6.967 9.048 1.186 19.183
retorno 2022-2026 Ciclo de alzas 1191 -0.021 -0.052 0.816 -2.944 4.820 0.443 1.812
volatilidad 2015-2019 Pre-pandemia 1278 12.917 12.115 4.319 5.072 24.332 0.446 -0.620
volatilidad 2020-2021 Pandemia 518 13.351 11.466 8.707 4.468 53.842 3.264 11.381
volatilidad 2022-2026 Ciclo de alzas 1191 12.312 11.562 3.804 4.719 23.999 0.630 -0.140
carry 2015-2019 Pre-pandemia 1299 4.196 4.360 1.830 1.800 7.460 0.284 -1.202
carry 2020-2021 Pandemia 518 2.163 1.695 0.645 1.650 4.150 1.214 0.607
carry 2022-2026 Ciclo de alzas 1191 6.342 6.420 1.381 2.920 8.420 -0.191 -1.028
par(mfrow = c(1, 2), mar = c(8, 4, 3, 1))
boxplot(volatilidad ~ periodo, data = datos, col = AZUL, las = 2,
        main = "Volatilidad por periodo", ylab = "%", xlab = "", cex.axis = .7)
boxplot(carry ~ periodo, data = datos, col = ORO, las = 2,
        main = "Carry de política por periodo", ylab = "pp", xlab = "", cex.axis = .7)

par(mfrow = c(1, 1))

Interpretación. El carry de política es la variable que mejor separa los periodos, y su trayectoria coincide con la historia monetaria conocida: alrededor de 4 puntos porcentuales antes de la pandemia, cerca de 2 durante el episodio de tasas bajas, y por encima de 6 en el ciclo de alzas posterior. La comparación entre grupos recupera con nitidez un hecho económico documentado.

En la volatilidad el contraste es más sutil e ilustra por qué la elección del indicador importa. Las medianas de los tres periodos son bastante parecidas, entre 11,5 % y 12,1 %, de modo que el comportamiento típico apenas cambió. Lo que sí cambió de forma drástica es la dispersión: durante la pandemia la desviación estándar más que duplica la de los otros dos periodos, y la curtosis del retorno pasa de valores cercanos a 1,2 y 1,8 en los periodos extremos a 19,2 durante la pandemia. Si el análisis se hubiera limitado a comparar medianas, ese episodio habría pasado inadvertido.

6.3 Síntesis comparativa

¿Qué indicador distingue a qué agrupación?
Criterio Separa por régimen Separa por periodo
Media del retorno No No
Desv. est. del retorno Sí, de forma marcada Moderadamente
Curtosis del retorno Sí, de forma marcada Sí, de forma marcada
Volatilidad (mediana) Poco
Carry (mediana) Sí (máximo en estrés local) Sí, de forma marcada

7 Tarea 4 y 5. Informe descriptivo

7.1 Patrones principales

Primero: la dirección no depende del grupo. En ninguna de las dos agrupaciones la media del retorno se aparta de manera apreciable de cero. Ni el estado del mercado ni la etapa macroeconómica ayudan a anticipar si el peso se apreciará o se depreciará mañana. Esto es coherente con la distribución casi equilibrada de la variable de dirección.

Segundo: la magnitud sí depende del grupo, y de manera sistemática. La desviación estándar del retorno crece de forma ordenada del régimen normal al de aversión al riesgo. El estado del mercado no informa sobre el signo, pero sí sobre la escala del movimiento.

Tercero: los extremos se concentran. La curtosis del retorno en los días de aversión al riesgo es varias veces mayor que en los demás grupos, y la volatilidad de ese mismo grupo muestra una media muy superior a su mediana. Los movimientos verdaderamente atípicos no están repartidos de forma uniforme en el tiempo.

Cuarto: la elección del indicador cambia la conclusión. En la comparación por periodo, las medianas de volatilidad sugieren estabilidad mientras que las desviaciones estándar revelan un episodio extraordinario. Describir una distribución asimétrica únicamente con su medida de centro oculta justamente lo que interesa.

7.2 Implicaciones para el propósito del estudio

El objetivo del proyecto es estimar un rango de negociación. Los resultados sostienen dos decisiones de diseño:

  1. El centro del rango no debe intentar anticipar la dirección. Ningún grupo muestra una media de retorno distinguible de cero, de modo que un ancla de mercado resulta preferible a un pronóstico direccional.

  2. El ancho del rango debe ser condicional. Como la dispersión difiere de manera clara entre regímenes, una banda de amplitud fija sería demasiado ancha en los días normales y demasiado estrecha justamente cuando el mercado se tensiona, que es cuando el error resulta más costoso.

7.3 Limitaciones

El análisis es descriptivo y exploratorio. Las diferencias observadas entre grupos no autorizan conclusiones causales: no se afirma que el régimen provoque mayor volatilidad, sino que ambos fenómenos se observan de manera conjunta. Las variables de agrupación son construidas a partir de umbrales muestrales, de modo que su definición depende del periodo analizado y otra ventana temporal produciría cortes distintos. Finalmente, los tamaños de los grupos son desiguales, lo que exige prudencia al comparar indicadores sensibles a valores extremos.


Base documentada en la Actividad 1.1.2. Fuentes: LSEG Workspace y Banco de la República. Procesamiento en R con los paquetes psych, summarytools y DT.