Introducción

El presente informe tiene como objetivo brindar un análisis cuantitativo para apoyar decisiones de inversión en el mercado bursátil colombiano, enfocado en cuatro segmentos estratégicos: petrolero, financiero, industrial y de servicios. El análisis se desarrolla en el marco del índice MSCI COLCAP, utilizando como referencia una acción representativa por cada sector.

Para ello, se emplean herramientas estadísticas y de analítica de datos, fundamentadas en métodos de pronóstico y teoría de la decisión. Los cálculos se basan en datos mensuales de precios de acciones y del Índice de Precios al Consumidor (IPC), lo cual permite establecer escenarios económicos realistas: favorables, estables y en declive, basados en niveles de inflación.

Se aplican diferentes modelos de pronóstico como promedios móviles, suavización exponencial, regresiones polinómicas y modelos ARIMA, con el fin de estimar los rendimientos esperados por acción bajo cada condición económica. Además, se calculan medidas de error para evaluar la precisión de los modelos.

El análisis se complementa con la construcción de un árbol de decisión, el cálculo del valor esperado (EV) por segmento y la realización de simulaciones de Monte Carlo, lo cual permite estimar el comportamiento futuro del portafolio bajo incertidumbre.

El resultado de este informe permitirá al inversionista, que dispone de $100 millones de pesos colombianos, tomar decisiones estratégicas con base en métricas cuantitativas, evaluando no solo el rendimiento potencial, sino también el riesgo asociado a cada alternativa.

El DataFrame

Para el desarrollo del presente estudio, se construyeron 7 datas frames consolidados con información mensual desde mayo de 2015 hasta abril de 2025, el cual constituye la base del análisis cuantitativo; Estos datos fueron obtenidos de las siguientes pagínas web:

  • Investing.com: es una plataforma de mercados financieros que proporciona datos en tiempo real, cotizaciones, gráficos, herramientas financieras, noticias de última hora y análisis de 250 mercados del mundo a través de sus 44 ediciones internacionales.

  • datosmacro.expansion.com: es un sitio web que reúne en un solo lugar todos los datos macroeconómicos. Ofrece información sobre el PIB, la inflación, ratings, deuda, hipotecas, paro, prima de riesgo, salarios, divisas y más, de forma sencilla de consultar. El objetivo es proporcionar una visión global de la situación económica en cada momento y en cada país.

Gracias a los datos obtenidos de estas páginas, creamos los siguientes conjunto de datos:

Descripción del Data Frame: Serie Histórica de BIC

Este data frame contiene información mensual detallada de la acción BIC (Bancolombia), una de las principales acciones del sector financiero colombiano, negociada en la Bolsa de Valores de Colombia (BVC). La serie se extiende desde mayo de 2015 hasta abril de 2025, proporcionando un histórico suficiente para análisis de tendencias, pronósticos y toma de decisiones estratégicas.

Estructura de columnas

Columna Descripción
Fecha Fecha de referencia mensual (último día hábil del mes).
Último Precio de cierre de la acción al finalizar el mes, en pesos colombianos.
Apertura Precio de apertura de la acción al inicio del mes.
Máximo Precio más alto registrado durante el mes.
Mínimo Precio más bajo registrado durante el mes.
Vol. Volumen total negociado durante el mes, usualmente en millones de acciones.
% var. Variación porcentual del precio de cierre respecto al mes anterior.
Rendimientos BIC Rendimiento mensual de la acción, calculado como:

\[ \text{Rendimiento}_t = \left( \frac{P_t - P_{t-1}}{P_{t-1}} \right) \times 100 \]

Nota: Aunque % var. y Rendimientos EC son muy similares, se incluyen ambas columnas para trazabilidad. En el análisis se toma Rendimientos EC como la fuente oficial de retorno mensual; ya que la columna % var. es la columna que por defecto se descargo de la página web

Observaciones adicionales

  • Los precios están expresados en pesos colombianos (COP).
  • El volumen (Vol.) está en formato abreviado (ej. 5,15M),

Pronóstico

Vamos a pronosticar los precios de cierre de las acciones seleccionadas utilizando diferentes métodos estadísticos y de series temporales. Para ello, se aplicaran técnicas como promedios móviles, suavización exponencial, análisis de índices estacionales, regresiones polinómicas, análisis de correlación y modelos ARIMA. Después de obtener las predicciones con cada método, se calculan las medidas de error de pronóstico (como MAE, RMSE, MAPE, etc.) para evaluar y comparar la precisión de cada técnica en cada acción.

# Cargar las librerías 
library(readxl)
library(forecast)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(ggplot2)
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(smooth)
## Cargando paquete requerido: greybox
## Package "greybox", v2.0.4 loaded.
## This is package "smooth", v4.2.0

Bancolombia (BIC)

Bancolombia S.A. es el grupo financiero más grande de Colombia y uno de los más importantes de América Latina. Ofrece una amplia gama de servicios financieros y bancarios tanto a personas como a empresas, incluyendo cuentas de ahorro, créditos, tarjetas, inversiones, seguros y banca digital.

# Cargar los datos
library(readxl)
Caso1 <- read_excel("Caso1.xlsx", sheet = "DatoshistoricosdeBanColombia")
(Caso1)
Caso1$`Rendimientos BIC` <- as.numeric(Caso1$`Rendimientos BIC`)
# Detectar año y mes de inicio
fecha_inicio <- min(Caso1$Fecha, na.rm = TRUE)
anio_inicio <- as.numeric(format(fecha_inicio, "%Y"))
mes_inicio <- as.numeric(format(fecha_inicio, "%m"))

# Convertir la columna Ultimo a numérica (aunque ya vimos que tiene 120 validos, no está de más ser explícito)
Caso1$`Rendimientos BIC` <- as.numeric(Caso1$`Rendimientos BIC`)


# Crear las series temporales con fecha correcta
# Asegúrate de que los datos estén ordenados por fecha si read_excel no lo garantiza
# Caso1 <- Caso1[order(Caso1$Fecha),] # Esto podría ser necesario si los datos no están ya ordenados

y <- ts(Caso1$`Rendimientos BIC`, frequency = 12, start = c(anio_inicio, mes_inicio))

# Fechas para graficar (aunque ts.plot puede manejar el eje x temporalmente)
# La variable 'fechas' no es estrictamente necesaria para ts.plot, pero puede ser util para otras graficas
fechas <- seq(as.Date(fecha_inicio), length.out = length(y), by = "months")

# Graficar con título, ejes y color
ts.plot(y,
        main = "Gráfico historico de los rendimientos de las acciones de Bancolombia", # Título del gráfico
        xlab = "Fecha",         # Nombre del eje X
        ylab = "Precio de cierre", # Nombre del eje Y
        col = "blue"            # Color de la línea (puedes cambiar "blue" por otro color)
)

Se puede hacer el siguiente análisis de la serie de tiempo:

  • Tendencia: La serie no muestra una tendencia clara de crecimiento o disminución a lo largo del tiempo. En términos generales, el comportamiento parece ser relativamente estable, sin una dirección sostenida hacia arriba o hacia abajo. Sin embargo, se pueden notar ciertos periodos de variabilidad significativa.

  • Irregularidad: La serie exhibe variaciones impredecibles, con algunos picos y caídas bruscas que no siguen un patrón definido. Estos cambios repentinos sugieren la presencia de ruido en los datos, lo que puede deberse a factores externos o eventos inesperados.

  • Estacionalidad: No se observan patrones recurrentes que se repitan en intervalos regulares, como ciclos anuales o mensuales. Los picos y valles parecen ocurrir de manera irregular, lo que sugiere que la serie no presenta una estacionalidad marcada.

  • Ciclicidad: Se identifican fluctuaciones a largo plazo, particularmente en torno al año 2020, donde se presenta un aumento repentino seguido de una caída pronunciada. Estas variaciones podrían estar influenciadas por factores económicos o eventos específicos, pero no parecen ocurrir en intervalos fijos.

Modelo de Promedios Moviles

#Modelos de suavizamiento
#Modelo de promedios móviles
m1 <- sma(y, order = 12) # Especificamos el tamaño de la ventana a 12
f1 <- forecast(m1, h = 12) # Pronosticamos los próximos 12 meses
plot(forecast(m1))

Suavización exponencial

# Ajustar el modelo de suavización exponencial simple
m2 <- ces(y, seasonality = "none") # No especificamos h aquí, ces ajustará el mejor modelo SES

# Definir cuántos períodos quieres pronosticar hacia el futuro (h) - DEBES ELEGIR UN NÚMERO
periodos_pronostico_ses <- 12 # Ejemplo: pronosticar los próximos 12 meses

# Generar el pronóstico usando la función forecast()
f2 <- forecast(m2, h = periodos_pronostico_ses) # Especificamos h aquí

# Imprimir el resumen del pronóstico
print(f2)
##              Jan         Feb         Mar         Apr         May         Jun
## 2025                                                 0.005841086 0.006283511
## 2026 0.005937640 0.005889261 0.005841277 0.005793683                        
##              Jul         Aug         Sep         Oct         Nov         Dec
## 2025 0.006236338 0.006185559 0.006135161 0.006085173 0.006035592 0.005986416
## 2026
# Graficar el pronóstico
plot(f2)

Modelos Indices estacionales

Simple

# Ajustar el modelo de suavización exponencial con estacionalidad simple
m3 <- ces(y, seasonality = "simple") # ces ajustará el mejor modelo SES con estacionalidad

# Definir cuántos períodos quieres pronosticar hacia el futuro (h) - DEBES ELEGIR UN NÚMERO
periodos_pronostico_ses_s <- 12 # Ejemplo: pronosticar los próximos 12 meses (ajusta este valor)

# Generar el pronóstico usando la función forecast()
f3 <- forecast(m3, h = periodos_pronostico_ses_s) # Especificamos h aquí

# Imprimir el resumen del pronóstico
print(f3)
##               Jan          Feb          Mar          Apr          May
## 2025                                                      0.020300990
## 2026  0.010896048  0.017105426  0.002950056  0.004635624             
##               Jun          Jul          Aug          Sep          Oct
## 2025 -0.001294089  0.005016969  0.045651597  0.019356866  0.014790446
## 2026                                                                 
##               Nov          Dec
## 2025  0.006186924 -0.020200236
## 2026
# Graficar el pronóstico
plot(f3)

Parcial

# Ajustar el modelo de suavización exponencial con estacionalidad parcial
m4 <- ces(y, seasonality = "partial") # ces ajustará el mejor modelo con estacionalidad parcial

# Definir cuántos períodos quieres pronosticar hacia el futuro (h)
periodos_pronostico_ces_p <- 12 # Pronosticamos los próximos 12 meses

# Generar el pronóstico usando la función forecast()
f4 <- forecast(m4, h = periodos_pronostico_ces_p) # Especificamos h aquí

# Imprimir el resumen del pronóstico
print(f4)
##               Jan          Feb          Mar          Apr          May
## 2025                                                      0.007472838
## 2026  0.012045335  0.026623330 -0.012131569 -0.007727352             
##               Jun          Jul          Aug          Sep          Oct
## 2025 -0.018317442 -0.019724057  0.023879666  0.049955463  0.028023977
## 2026                                                                 
##               Nov          Dec
## 2025 -0.000453616 -0.020518428
## 2026
# Graficar el pronóstico
plot(f4)

Full

# Ajustar el modelo de suavización exponencial con estacionalidad completa
m5 <- ces(y, seasonality = "full") # ces ajustará el mejor modelo con estacionalidad completa (Holt-Winters similar)

# Definir cuántos períodos quieres pronosticar hacia el futuro (h)
periodos_pronostico_ces_f <- 12 # Pronosticamos los próximos 12 meses

# Generar el pronóstico usando la función forecast()
f5 <- forecast(m5, h = periodos_pronostico_ces_f) # Especificamos h aquí

# Imprimir el resumen del pronóstico
print(f5)
##                Jan           Feb           Mar           Apr           May
## 2025                                                          0.0063612499
## 2026  0.0137459597  0.0181715984 -0.0018122859  0.0062599297              
##                Jun           Jul           Aug           Sep           Oct
## 2025  0.0007840827 -0.0019299321  0.0071320028  0.0292868507  0.0144571420
## 2026                                                                      
##                Nov           Dec
## 2025  0.0068280943  0.0022103247
## 2026
# Graficar el pronóstico
plot(f5)

print(f5$mean)
##                Jan           Feb           Mar           Apr           May
## 2025                                                          0.0063612499
## 2026  0.0137459597  0.0181715984 -0.0018122859  0.0062599297              
##                Jun           Jul           Aug           Sep           Oct
## 2025  0.0007840827 -0.0019299321  0.0071320028  0.0292868507  0.0144571420
## 2026                                                                      
##                Nov           Dec
## 2025  0.0068280943  0.0022103247
## 2026

El pronóstico reflejado en el gráfico muestra un comportamiento estable a lo largo del periodo proyectado, sin tendencias crecientes ni decrecientes significativas. La serie histórica evidencia ciertas fluctuaciones, con picos y valles pronunciados en años como 2018 y 2020, lo que indica variabilidad en los datos previos. Sin embargo, el modelo de pronóstico CES (full) con distribución normal sugiere que las fluctuaciones futuras se mantendrán en un rango estrecho, sin grandes desviaciones. En términos de patrones estacionales, los valores pronosticados presentan ligeras variaciones mes a mes, aunque no hay una estacionalidad claramente definida. Algunos valores atípicos pueden observarse en meses como septiembre de 2025, donde el pronóstico es notablemente más alto (0.0293), comparado con otros meses. En general, el pronóstico se mantiene alrededor de cero con pequeñas oscilaciones, lo que indica que el modelo no anticipa cambios drásticos ni puntos de cambio relevantes en la tendencia de la serie. Esto sugiere que la dinámica del sistema analizado continuará con un comportamiento estable en el futuro cercano, con ligeras variaciones dentro de un margen controlado.

Regresiones polinómicas

#Modelos polinomicos y
T=length(y)
t = seq(1:T) 
t2 = t^2 
t3 = t^3
t4 = t^4
t5 = t^5
t6 = t^6
t7 = t^7
t8 = t^8

mlineal=lm(y~t)
summary(mlineal)
## 
## Call:
## lm(formula = y ~ t)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.39141 -0.04207  0.00549  0.04894  0.25324 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)
## (Intercept)  0.0171637  0.0155905   1.101    0.273
## t           -0.0001259  0.0002236  -0.563    0.575
## 
## Residual standard error: 0.08486 on 118 degrees of freedom
## Multiple R-squared:  0.002677,   Adjusted R-squared:  -0.005775 
## F-statistic: 0.3167 on 1 and 118 DF,  p-value: 0.5746
g1<-predict.lm(mlineal, h=12)
mcuad=lm(y~t+t2)
summary(mcuad)
## 
## Call:
## lm(formula = y ~ t + t2)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.38736 -0.04055  0.00427  0.04643  0.25710 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)
## (Intercept)  2.548e-02  2.371e-02   1.075    0.285
## t           -5.349e-04  9.046e-04  -0.591    0.555
## t2           3.381e-06  7.243e-06   0.467    0.642
## 
## Residual standard error: 0.08514 on 117 degrees of freedom
## Multiple R-squared:  0.004531,   Adjusted R-squared:  -0.01249 
## F-statistic: 0.2663 on 2 and 117 DF,  p-value: 0.7667
g2<-predict.lm(mcuad, h=12)
#Grafico sencillo
plot(fechas,y,type="l",col="#828282")
lines(fechas,g1,col="#007bff")
lines(fechas,g2,col="#4c3d19") 

Modelo ARIMA

library(tseries)
adf.test(y)  # Prueba de Dickey-Fuller aumentada
## Warning in adf.test(y): p-value smaller than printed p-value
## 
##  Augmented Dickey-Fuller Test
## 
## data:  y
## Dickey-Fuller = -5.788, Lag order = 4, p-value = 0.01
## alternative hypothesis: stationary

El resultado del Augmented Dickey-Fuller Test indica que la serie temporal analizada es estacionaria. El valor del estadístico Dickey-Fuller (-5.788) es significativamente bajo, lo que sugiere una fuerte evidencia en favor de la estacionariedad. Además, el p-value obtenido (0.01) es inferior al umbral común de 0.05, lo que permite rechazar la hipótesis nula de no estacionariedad con un alto nivel de confianza. El orden de rezago utilizado en la prueba es 4, lo que indica que la serie se ha evaluado considerando la posible influencia de hasta cuatro periodos anteriores en su comportamiento. En términos prácticos, este resultado sugiere que la serie no presenta una tendencia persistente o una estructura de raíz unitaria que afecte su capacidad para ser modelada mediante métodos de pronóstico sin necesidad de diferenciación adicional.

modelo_arima <- auto.arima(y)
summary(modelo_arima)
## Series: y 
## ARIMA(0,0,0) with zero mean 
## 
## sigma^2 = 0.007191:  log likelihood = 125.82
## AIC=-249.64   AICc=-249.61   BIC=-246.85
## 
## Training set error measures:
##                      ME       RMSE        MAE MPE MAPE      MASE       ACF1
## Training set 0.00954913 0.08480176 0.06212656 100  100 0.6295685 -0.1147402
pronostico <- forecast(modelo_arima, h = 12)  # Pronóstico para 12 meses
plot(pronostico, main = "Pronóstico ARIMA rendimientos Bancolombia")

# Si quieres ver los valores del pronóstico:
print(pronostico)
##          Point Forecast      Lo 80     Hi 80      Lo 95     Hi 95
## May 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Jun 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Jul 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Aug 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Sep 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Oct 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Nov 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Dec 2025              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Jan 2026              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Feb 2026              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Mar 2026              0 -0.1086778 0.1086778 -0.1662084 0.1662084
## Apr 2026              0 -0.1086778 0.1086778 -0.1662084 0.1662084

Análisis de correlación

  y<-(Caso1$`Rendimientos BIC`)
x<-(Caso1$`IPC mensual`)
mcorr<-lm(y~x)
summary(mcorr)
## 
## Call:
## lm(formula = y ~ x)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.39372 -0.04407  0.00279  0.05133  0.24958 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)
## (Intercept)  0.01587    0.01641   0.967    0.335
## x           -0.10784    0.24677  -0.437    0.663
## 
## Residual standard error: 0.0849 on 118 degrees of freedom
## Multiple R-squared:  0.001616,   Adjusted R-squared:  -0.006845 
## F-statistic: 0.191 on 1 and 118 DF,  p-value: 0.6629

Esta correlación también indica ausencia de relación lineal significativa entre x e y:

El coeficiente de x es -0.10784, muy cercano a cero y no significativo (p = 0.663).

R² = 0.0016, es decir, x explica apenas el 0.16% de la variabilidad en y.

El modelo completo no es significativo (F = 0.191, p = 0.6629).

Conclusión breve: No hay evidencia de una relación lineal entre las variables; la correlación es prácticamente nula.

# Calcular la correlación de Pearson
correlacion_pearson <- cor(Caso1$`Rendimientos BIC`, 
                           Caso1$`IPC mensual`, 
                           use = "complete.obs", 
                           method = "pearson")
print(paste("Correlación de Pearson:", correlacion_pearson))
## [1] "Correlación de Pearson: -0.0401986406846348"

El coeficiente de Pearson -0.04 indica una correlación negativa extremadamente débil, prácticamente inexistente.

Esto refuerza lo mostrado por el modelo lineal: no hay relación lineal significativa entre x e y, ni desde un punto de vista estadístico ni práctico.

Medidas de error de pronóstico

# Función para calcular las medidas de error
calcular_errores <- function(real, pred) {
  mae <- mean(abs(real - pred))
  rmse <- sqrt(mean((real - pred)^2))
  mape <- mean(abs((real - pred)/real)) * 100
  return(data.frame(MAE = mae, RMSE = rmse, MAPE = mape))
}

# Para cada modelo, usa los valores ajustados (fitted) o predichos (forecast)
# Ejemplo para SMA:
errores_sma <- calcular_errores(y, fitted(m1))
errores_ces <- calcular_errores(y, fitted(m2))
errores_ces_estacional <- calcular_errores(y, fitted(m3))
errores_ces_parcial <- calcular_errores(y, fitted(m4))
errores_ces_full <- calcular_errores(y, fitted(m5))

# Para modelos polinómicos:
errores_lineal <- calcular_errores(y, g1)
errores_cuadratico <- calcular_errores(y, g2)

# Para ARIMA:
errores_arima <- calcular_errores(y, fitted(modelo_arima))
# Mostrar todos los resultados en una tabla
tabla_errores <- rbind(
  SMA = errores_sma,
  CES = errores_ces,
  CES_Estacional = errores_ces_estacional,
  CES_Parcial = errores_ces_parcial,
  CES_Full = errores_ces_full,
  Polinomial_Lineal = errores_lineal,
  Polinomial_Cuadratico = errores_cuadratico,

  ARIMA = errores_arima
)

knitr::kable(tabla_errores, caption = "Medidas de error de pronóstico para cada modelo Bancolombia")
Medidas de error de pronóstico para cada modelo Bancolombia
MAE RMSE MAPE
SMA 0.0655110 0.0886676 149.5219
CES 0.0609227 0.0839856 111.5166
CES_Estacional 0.0611865 0.0849478 121.2231
CES_Parcial 0.0598043 0.0819117 118.9176
CES_Full 0.0596995 0.0824098 103.4841
Polinomial_Lineal 0.0609494 0.0841495 108.1645
Polinomial_Cuadratico 0.0608185 0.0840713 106.0647
ARIMA 0.0621266 0.0848018 100.0000

Elección del mejor modelo

# --- Elección del mejor modelo por cada métrica ---

# Asumimos que tabla_errores es tu data.frame con modelos como nombres de fila
# y métricas (RMSE, R2, MAE, etc.) como columnas.
# Para este ejemplo, asumimos que un valor MENOR es mejor para todas las métricas,
# excepto quizás R2 donde un valor MAYOR es mejor.

print("--- Análisis de Modelos por Métrica ---")
## [1] "--- Análisis de Modelos por Métrica ---"
# Obtener los nombres de las métricas (nombres de columna)
metricas <- colnames(tabla_errores)

# Iterar sobre cada métrica
for (metrica in metricas) {

  # Determinar si un valor menor es mejor (generalmente cierto para errores)
  # o si un valor mayor es mejor (como para R-squared).
  # Puedes añadir lógica aquí si tienes métricas específicas que maximizar.
  # Por defecto, asumimos que menor es mejor.
  menor_es_mejor <- TRUE
  if (metrica == "R2" || metrica == "R-squared") { # Ejemplo: R2 es mejor cuanto mayor
      menor_es_mejor <- FALSE
  }

  if (menor_es_mejor) {
      # Encontrar el índice (número de fila) del modelo con el menor valor en la métrica actual
      indice_mejor_metrica <- which.min(tabla_errores[[metrica]])
      criterio_mejor <- "menor"
  } else {
      # Encontrar el índice (número de fila) del modelo con el mayor valor en la métrica actual
      indice_mejor_metrica <- which.max(tabla_errores[[metrica]])
      criterio_mejor <- "mayor"
  }


  # Usar ese índice para obtener el nombre del modelo
  mejor_modelo_metrica <- rownames(tabla_errores)[indice_mejor_metrica]

  # Obtener el valor de la métrica para ese modelo
  valor_mejor_metrica <- tabla_errores[indice_mejor_metrica, metrica]

  # Imprimir el resultado para la métrica actual
  print(paste0("El modelo con el ", criterio_mejor, " ", metrica, " es: ", mejor_modelo_metrica))
  print(paste0("Su valor de ", metrica, " es: ", round(valor_mejor_metrica, 4))) # Redondeamos para mejor visualización

  print("--------------------") # Separador para cada métrica
}
## [1] "El modelo con el menor MAE es: CES_Full"
## [1] "Su valor de MAE es: 0.0597"
## [1] "--------------------"
## [1] "El modelo con el menor RMSE es: CES_Parcial"
## [1] "Su valor de RMSE es: 0.0819"
## [1] "--------------------"
## [1] "El modelo con el menor MAPE es: ARIMA"
## [1] "Su valor de MAPE es: 100"
## [1] "--------------------"
# Nota: Este código identifica el mejor modelo PARA CADA MÉTRICA INDIVIDUALMENTE.
# No elige un único "mejor modelo general" si diferentes métricas apuntan a modelos distintos.
# Si necesitas una decisión final basada en múltiples métricas, podrías considerar
# alguna forma de puntuación o ranking combinado.

Entre los modelos evaluados para pronosticar las acciones de Bancolombia, el modelo CES_Full presenta el mejor desempeño general, con el menor MAE (0.0597) y un bajo RMSE (0.0824), lo que indica mayor precisión en términos absolutos. Aunque su MAPE (103.48%) no es el más bajo, es significativamente mejor que otros modelos como el SMA y las variantes estacionales o parciales del CES. Dado que el modelo ARIMA genera un pronóstico constante y no aporta valor adicional en este caso, CES_Full se posiciona como la mejor opción, al ofrecer un equilibrio sólido entre precisión y adaptación al comportamiento de la serie.