Modelos de Series de Tiempo

Análisis Exploratorio de Datos

Análisis inicial de la serie

library(readr)
library(TSstudio)
library(dplyr)
library(knitr)

Los datos a utilizar corresponden a un dataset que contiene el valor de las acciones de Tecnoglass, a lo largo de varios años.

tcglss <- read_csv("https://raw.githubusercontent.com/lihkir/Data/main/TGLS.csv")
tcglss %>% head() %>%  kable()
Date Open High Low Close Adj Close Volume
2012-05-10 9.97 10.00 9.50 9.80 7.945550 6900
2012-05-11 9.70 9.70 9.70 9.70 7.864471 300
2012-05-14 9.80 9.80 9.80 9.80 7.945550 100
2012-05-15 9.75 9.75 9.75 9.75 7.905012 300
2012-05-16 9.75 9.75 9.75 9.75 7.905012 0
2012-05-17 9.60 9.60 9.60 9.60 7.783393 800

Para trabajar con la serie de tiempo de Tecnoglass, primero se debe convertir la columna de interés, Close, a un objeto tipo ts.

# convertir campo en variable ts
tcglss_ts <- ts(tcglss$Close)
class(tcglss_ts)
## [1] "ts"

Creación de train y test

Se extrae una muestra con los últimos 7 datos de la serie de tiempo, para evaluar mas adelante los modelos predictivos.

split_tcglss <- ts_split(tcglss_ts, sample.out = 7)
train <- split_tcglss$train
test <- split_tcglss$test
test
## Time Series:
## Start = 2194 
## End = 2200 
## Frequency = 1 
## [1] 7.00 6.79 7.00 7.02 7.07 7.15 7.15

Visualización de la serie

A continuación se muestra como es la serie de tiempo para el campo Close.

ts_plot(tcglss_ts,
        title = "Valor de las acciones de Tecnoglass",
        Ytitle = "Acciones",
        Xtitle = "Día", Xgrid = T, )

Al observar la serie de tiempo no logra identificar un patrón estacional marcado. Inicialmente la serie tiene un comportamiento levemente estacionario, luego tiene una tendencia a la alta, para luego pasar a una tendencia a la baja, repitiéndose este comportamiento de alza y baja en lo siguientes periodos de la serie. En los últimos meses de la serie, se mantiene la tendencia a la alza.

summary(tcglss_ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   2.290   7.960   9.840   9.477  10.910  15.060
#desviación de la serie de tiempo
sd(tcglss_ts)
## [1] 2.300886
hist(tcglss_ts, main = "Histograma de Acciones Tecnoglass", xlab = "Acciones", breaks = "Sturges", probability = TRUE)
lines(density(tcglss_ts))

Se puede observar tanto en el resumen estadístico como en el histograma, que los datos tienden a una distribución simétrica, y se aglomeran principalmente alrededor de 9.

  • Verificar si hay valores perdidos
sum(is.na(tcglss_ts))
## [1] 0
  • Frecuencia de la serie
frequency(tcglss_ts)
## [1] 1
  • Ciclo de la serie
cycle(tcglss_ts)
## Time Series:
## Start = 1 
## End = 2200 
## Frequency = 1 
##    [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##   [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##   [75] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [112] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [149] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [186] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [223] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [260] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [297] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [334] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [371] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [408] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [445] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [482] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [519] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [556] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [593] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [630] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [667] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [704] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [741] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [778] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [815] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [852] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [889] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [926] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [963] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1000] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1037] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1074] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1111] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1148] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1185] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1222] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1259] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1296] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1333] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1370] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1407] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1444] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1481] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1518] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1555] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1592] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1629] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1666] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1703] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1740] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1777] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1814] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1851] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1888] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1925] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1962] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1999] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2036] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2073] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2110] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2147] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2184] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

No hay valores nulo en la serie de tiempo. La frecuencia de la serie es de 1 días, las fechas de la seria de tiempo no son continuas, y no se logra apreciar un patrón estacional que indique una frecuencia mas alta.

Descomposición

Como la serie tiene frecuencia 1, no es posible obtener el gráfico de descomposición, principalmente porque el componente estacional requiere que para cada periodo se tenga 2 o más registros que permitan detectar algún patrón en los datos.

Análisis de Estacionariedad

Dicky-Fuller

Para poder realizar un modelo ARIMA para la serie de tiempo, se requiere que la serie sea estacionaria. A simple vista, se logra apreciar que la serie no es estacionaria, pero para confirmarlo se hará uso del test de Dicky Fuller.

Para este test se plantean las siguientes hipótesis:

  • \(H_{0}\): La serie de tiempo no es estacionario
  • \(H_{1}\): La serie de tiempo es estacionario
library(tseries)
adf.test(train)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  train
## Dickey-Fuller = -2.7758, Lag order = 12, p-value = 0.2499
## alternative hypothesis: stationary

Observando el p-valor obtenido, 0.2499, y tomando un valor de significancia de 0.05, no se puede rechazar la hipótesis nula que afirma que la serie no es estacionaria.

Autocorrelación

Por medio del gráfico de Autocorrelación (ACF), también se puede verificar si la seria es estacionaria:

ts_cor(train, type = "acf")

Se puede ver en el gráfico ACF que la correlación de la seria con sus rezagos decae lentamente, y no llega a cero, por lo que efectivamente la serie no es estacionaria.

Como la serie no es estacionaria, se debe aplicar técnicas de transformación para convertir la serie en estacionaria y poder trabajar con ella.

Diferenciación

Para transformar la serie de tiempo, se aplica inicialmente una sola diferenciación a los datos.

tcglss_ts_d1 <- diff(train)

Se verifica con el test de Dicky-Fuller si la serie es estacionaria:

adf.test(tcglss_ts_d1)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  tcglss_ts_d1
## Dickey-Fuller = -13.324, Lag order = 12, p-value = 0.01
## alternative hypothesis: stationary

Tomando un valor de significancia de 0.05, se rechaza la hipótesis nula que afirma que la serie no es estacionaria.

Ahora observamos los gráficos ACF y PACF de la nueva serie de tiempo:

par(mfrow=c(1,2))
ts_cor(tcglss_ts_d1 )
# pacf(tcglss_ts_d1)

El nuevo gráfico ACF muestra que la seria se convirtió en estacionaria exitosamente.

Selección del Modelo

Para la selección del modelo se probaran diferentes enfoques.

auto.arima

Primero se utiliza la función auto.arima para identificar el mejor modelo ARIMA. Para tener una mayor variedad de opciones de prueba, se coloca FALSE en el parámetro stepwise.

library(forecast)
tcglss_auto_md <- auto.arima(train,
                             stepwise = FALSE,
                             approximation = FALSE)
tcglss_auto_md
## Series: train 
## ARIMA(1,1,0) 
## 
## Coefficients:
##           ar1
##       -0.0689
## s.e.   0.0213
## 
## sigma^2 = 0.04213:  log likelihood = 361.17
## AIC=-718.34   AICc=-718.33   BIC=-706.95

Para este caso, el modelo ARIMA que entrega la menor métrica AIC, con un valor de -718.34. El modelo seleccionado por la función es un Modelo ARIMA (1,1,0)

Ahora se realizan las predicciones para un horizonte de 7 dias con este modelo:

tcglss_test_fc <- forecast(tcglss_auto_md, h = 7)
tcglss_test_fc %>% kable()
Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
2194 7.031710 6.768658 7.294761 6.629408 7.434012
2195 7.030903 6.671476 7.390330 6.481207 7.580599
2196 7.030959 6.595307 7.466610 6.364687 7.697230
2197 7.030955 6.530600 7.531309 6.265728 7.796181
2198 7.030955 6.473353 7.588557 6.178176 7.883734
2199 7.030955 6.421459 7.640451 6.098812 7.963099
2200 7.030955 6.373650 7.688260 6.025693 8.036217

Se calculan las métricas de evaluación del modelo:

accuracy(tcglss_test_fc, test) %>% kable()
ME RMSE MAE MPE MAPE MASE ACF1 Theil’s U
Training set -0.0013451 0.2051663 0.1210993 -0.0557208 1.437541 0.9969568 0.0014933 NA
Test set -0.0053416 0.1133806 0.0845230 -0.1024331 1.211630 0.6958402 0.3690249 0.9383515
  • Calculo del \(R^{2}\)
rsq <- function (x, y) cor(x, y) ^ 2
rsq_auto <- rsq(tcglss_test_fc$mean, test)
rsq_auto
## [1] 0.001166363

El \(R^{2}\) para este modelo es muy bajo, lo que indica que no es muy bueno para explicar la variabialidad de la serie de tiempo.

En el siguiente gráfico se puede observar la serie de tiempo, la predicción y el valor real de los valores predichos.

test_forecast(tcglss_ts,
              forecast.obj = tcglss_test_fc,
              test = test)

Se puede observar que los valores predicho se mantienen contaste después de la cuarta observación, y que la predicciones no son muy buenas con respecto al valor real.

Ahora se verificará que los residuos del modelo satisfacen los siguientes supuestos:

  • Independencia de los residuos
  • Media cero y varianza constante
  • Normalidad de los residuos
checkresiduals(tcglss_auto_md)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,1,0)
## Q* = 5.1383, df = 9, p-value = 0.8221
## 
## Model df: 1.   Total lags used: 10

Los residuos del modelo no muestran ningún patrón especifique y es solo ruido blanco, con media cero. Se puede ver en el gráfico de densidad que los residuos siguen una distribución normal. Para el caso de la prueba de Ljung-Box, se tiene un p-valor de 0.8221, lo que significa que para cualquier valor de significancia no se rechaza la hipótesis nula que afirma que los residuos son independiente, por lo tanto no hay correlación entre los residuos. El modelo satisface todos los supuestos.

Iteración de modelos

El otro método que se utilizará para seleccionar el mejor modelo, es a través de una función iterativa que buscará la mejor combinación de parámetros (p,d,q) que lográ minimizar el coeficiente AIC.

# función que busca el mejor modelo
best_ARIMA <- function(ts_in, p_n, d_n, q_n) {
  best_aic <- Inf
  best_pdq <- NULL
  fit <- NULL
  for(p in 1:p_n) {
    for(d in 1:d_n) {
      for (q in 1:q_n) {

              tryCatch({
                fit <- arima(scale(ts_in), 
                             order=c(p, d, q), 
                             xreg=1:length(ts_in), 
                             method="CSS-ML")
                tmp_aic <- AIC(fit)
                if (tmp_aic < best_aic) {
                  best_aic <- tmp_aic
                  best_pdq = c(p, d, q)
                }
              }, error=function(e){})

      }
    }
  }
  return(list("best_aic" = best_aic, "best_pdq" = best_pdq))
}

Ahora se utiliza la función para seleccionar el mejor modelo:

# selección del mejor modelo
best_model <- NULL
best_model = best_ARIMA(train, 2, 2, 2)
best_model
## $best_aic
## [1] -4368.109
## 
## $best_pdq
## [1] 1 1 1

El modelo con el mejor coeficiente AIC es un modelo ARIMA de orden (1,1,1). A continuación se ajusta el modelo con esos parámetros.

fit_model <- arima(train, order=best_model$best_pdq)
fit_model
## 
## Call:
## arima(x = train, order = best_model$best_pdq)
## 
## Coefficients:
##           ar1     ma1
##       -0.4373  0.3707
## s.e.   0.2201  0.2276
## 
## sigma^2 estimated as 0.04208:  log likelihood = 361.92,  aic = -717.84
tcglss_test_fc_2 <- forecast(fit_model, h = 7)
tcglss_test_fc_2 %>% kable()
Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
2194 7.030793 6.767892 7.293694 6.628720 7.432866
2195 7.026073 6.666442 7.385704 6.476064 7.576081
2196 7.028137 6.588400 7.467874 6.355617 7.700657
2197 7.027234 6.521547 7.532922 6.253853 7.800616
2198 7.027629 6.463002 7.592256 6.164107 7.891152
2199 7.027457 6.409746 7.645167 6.082750 7.972164
2200 7.027532 6.360846 7.694218 6.007924 8.047140
accuracy(tcglss_test_fc_2, test) %>% kable()
ME RMSE MAE MPE MAPE MASE ACF1 Theil’s U
Training set -0.0013211 0.2050963 0.1211255 -0.0545871 1.436596 0.9971726 -0.0004277 NA
Test set -0.0021222 0.1129685 0.0842314 -0.0565214 1.206818 0.6934395 0.3782479 0.9348401
  • Calculo del \(R^{2}\)
rsq <- function (x, y) cor(x, y) ^ 2
rsq_auto <- rsq(tcglss_test_fc_2$mean, test)
rsq_auto
## [1] 0.05147613

Para este modelo el \(R^{2}\) también es relativamente bajo, pero moderadamente mas alto que el \(R^{2}\) obtenido con el modelo de auto.arima. Esto indica que este modelo logra explicar alrededor del 5% de la variabilidad de la serie de tiempo.

En el siguiente gráfico se puede observar la serie de tiempo, la predicción y el valor real de los valores predichos.

test_forecast(tcglss_ts,
              forecast.obj = tcglss_test_fc_2,
              test = test)

Las predicciones con este modelo tampoco se logran ajustar a los valores reales de la serie de tiempo para el horizonte predicho. Contrario a las predicciones del modelo de auto.arima, estas predicciones presenten variabilidad en los datos, y no son constantes.

Ahora se verificará que los residuos del modelo satisfacen los supuestos:

checkresiduals(fit_model)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,1,1)
## Q* = 3.6312, df = 8, p-value = 0.8888
## 
## Model df: 2.   Total lags used: 10

Los residuos del modelo no muestran ningún patrón especifique y es solo ruido blanco, con media cero. Se puede ver en el gráfico de densidad que los residuos siguen una distribución normal. Para el caso de la prueba de Ljung-Box, se tiene un p-valor de 0.888, lo que significa que para cualquier valor de significancia no se rechaza la hipótesis nula que afirma que los residuos son independiente, por lo tanto no hay correlación entre los residuos. El modelo satisface todos los supuestos.

  • Comparación de métricas
accuracy(tcglss_test_fc, test) %>% as.data.frame() %>% kable()
ME RMSE MAE MPE MAPE MASE ACF1 Theil’s U
Training set -0.0013451 0.2051663 0.1210993 -0.0557208 1.437541 0.9969568 0.0014933 NA
Test set -0.0053416 0.1133806 0.0845230 -0.1024331 1.211630 0.6958402 0.3690249 0.9383515
accuracy(tcglss_test_fc_2, test) %>% as.data.frame() %>% kable()
ME RMSE MAE MPE MAPE MASE ACF1 Theil’s U
Training set -0.0013211 0.2050963 0.1211255 -0.0545871 1.436596 0.9971726 -0.0004277 NA
Test set -0.0021222 0.1129685 0.0842314 -0.0565214 1.206818 0.6934395 0.3782479 0.9348401

Las métricas obtenidas con el modelo ARIMA de orden (1,1,1) son ligeramente menores a las métricas del modelo de auto.arima, para el caso de los datos de prueba (test). Tambien ek modelo de order (1,1,1) tiene un \(R^{2}\) mayor al de modelo de auto.arima (0.05 > 0.001). Para el caso del coeficiente AIC, el puntaje obtenido con el modelo arima es un poco menor al del otro modelo, con un valor de -718.34, frente a -717.84, del modelo de orden (1,1,1). Como el modelo de orden(1,1,1) tiene mejores resultados en la mayoría de métricas, y su valor de AIC no es muy distinto al del modelo de auto.arima, se escoge el modelo ARIMA (1,1,1) par realizar predicciones.

Cabe resaltar que ninguno de los modelo es realmente optimo para realizar predicciones, por los valores tan bajos de \(R^{2}\).

Predicciones

Una vez seleccionado el modelo ARIMA (1,1,1), se procede a realizar una predicción para un horizonte de 7 días, tomando toda la serie de tiempo.

Primero se entrena el modelo neuvamente, con los parametros escogidos y tomando toda la serie:

final_md <- arima(tcglss_ts, order = c(1,1,1))

Luego se verifican los supuestos de los residuos.

checkresiduals(final_md)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,1,1)
## Q* = 3.5884, df = 8, p-value = 0.8922
## 
## Model df: 2.   Total lags used: 10

Los residuos cumplen con todos los supuestos: normalidad em la distribución, media cero en los datos, e independencia de los residuos, como se puede observar por el p-valor (0.892) obtenido en la prueba de Ljung-Box.

Y finalmente se realiza la predicción:

tcglss_fc <- forecast(final_md, h = 7)

En el siguiente gráfico se logra apreciar la predicción para el horizonte de 7 días, acompañados de los intervalos de predicción del 80% y 95%.

plot_forecast(tcglss_fc,
              title = "Valor de acciones de Tecnoglass - Forecast",
              Ytitle = "Acciones",
              Xtitle = "Día")

La predicción obtenida para los siguientes 7 días parece constante, y no parece captar la variabilidad de la serie de tiempo de las acciones de Tecnoglass. Se podría intentar encontrar algún tipo de componente estacional en la serie de tiempo que permita captar mejor el comportamiento de la misma, ya que la serie se trabajó con una frecuencia de 1, considerando que los registros no son de fechas consecutivas, y no se detecto en una primera instancia una periodicidad determinada.