Series de tiempo con acciones de Tecnoglass

Visualización Científica

Autor/a
Afiliación

Gabriela Díaz and Katherine Taján

Fecha de publicación

15 de abril de 2024

Para este informe, se utilizarán las siguientes librerías

library(dbplyr)
library(quantmod)
library(kableExtra)
library(TSstudio)
library(tidyverse)
library(yfR)
library(lubridate)
library(tseries)
library(plotly)
library(forecast)

Paso 1

Considere la serie de tiempo asociada con las acciones de Tecnoglass desde que comenzó a comercializarse hasta la fecha del día de hoy.

Se importan los datos desde Yahoo Finance con el paquete yfR:

tecno <- yf_get(tickers = 'TGLS',
                first_date = '2012-01-01',
                last_date = '2024-01-01'
)

tecno1_ts <- ts(tecno$price_close, 
                start = c(year(min(tecno$ref_date))),
                end = c(year(max(tecno$ref_date))),
                frequency = 252)

head(tecno, 10)

Nota: Es importante anotar que se toma una frecuencia de 252 días debido a que la bolsa de valores no abre los fines de semana, además de tener en cuenta los años bisiestos entre 2012 y 2023 (3 años bisiestos).

A continuación, se visualiza la serie de tiempo del precio de cierre de Tecnoglass desde 2012 hasta 2024

tecno1 <- tecno%>%
  select(ref_date, price_close)

ts_plot(tecno1_ts,
        title = "Monthly Close Price 2012-2024",
        Ytitle = 'Close price in USD',
        Xtitle = 'Year',
        color = '#006633')

Se puede observar que la serie temporal presenta una tendencia a la alza tanto aditiva como multiplicativa a patir del año 2020, en donde se pasó de un cierre de 10 dólares a un cierre de aproximandamente 46 dólares. Esto muestra que la serie está en un estado no estacionario.

Resumen del precio de cierre:

summary(tecno1_ts)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   2.29    8.62   10.05   12.53   12.56   48.36 

A continuación, se muestra la frecuencia de los precios de cierre a lo largo de los años

hist(tecno1_ts, main = "Histogram for Close Price", xlab = "Close price", breaks = "Sturges", probability = TRUE, col = '#9999ff')
lines(density(tecno1$price_close), col = 'red')

A través del histograma, se puede evidenciar la estabilidad que había en el precio de cierre que había alrededor de los 10 dólares. Esto concuerda con el com

Además, puede observar que no hay datos faltantes para el precio de cierre

sum(is.na(tecno1_ts))
[1] 0

S presenta un gráfico de vela del precio de cierre:

fig <- tecno %>% plot_ly(x = ~ref_date, type="candlestick",
                        open = ~price_open, close = ~price_close,
                        high = ~price_high, low = ~price_low) 
fig <- fig %>% layout(title = "Close Price Candlestic",
                      xaxis = list(title = 'Day'),
                      yaxis = list(title = 'Close price in USD'))
fig

Paso 2

Repita TODOS los pasos indicados en esta sección para encontrar modelos ARIMA para predecir el precio de las acciones de Tecnoglass con los siguientes horizontes: 7, 14 días, 21 días, 28 días. Utilizar siempre predicciones usando rolling con ventana de predicción continua de un día. Cualquier cantidad de pasos extra para enriquecer su análisis predictivo serán aceptados siempre y cuando sean acordes con lo que indica la teoría de análisis de series de tiempo.

Para empezar, se aplica una prueba estadística para verificar si la serie de tiempo es estacionaria o no. Esta prueba es la de Dickey-Fuller. En esta prueba, se toman las siguientes hipótesis:

\[H_0: \textrm{La serie no es estacionaria}\] \[H_1: \textrm{La serie es estacionaria}\]

adf1 <- adf.test(tecno1_ts)
adf1

    Augmented Dickey-Fuller Test

data:  tecno1_ts
Dickey-Fuller = 0.46747, Lag order = 14, p-value = 0.99
alternative hypothesis: stationary
acf(tecno1_ts, lag.max = 120, ylim = c(-0.5, 1))

Teniendo en cuenta que el p-valor es de 0.99 > 0.05, no se rechaza la hipótesis nula, por lo que se puede concluir con un 95% de confianza que la serie temporal del precio de cierre no es estacionaria. Además, observando que la gráfica de ACF baja desde la parte positiva con una, aunque leve, tendencia lineal, se puede confirmar el comportamiento no estacionario obtenido con la prueba de Dickey Fuller.

Diferenciación de la serie temporal

Se realizan ahora figuras de autocorrelación para confirmar que la serie de tiempo diferenciada es estacionaria, así como también para verificar cuál es el orden de integración necesario para llevar la serie de tiempo no estacionaria a una estacionaría.

par(mfrow=c(2,2))
acf(diff(tecno1_ts), lag.max = 240, main = 'First differentation')
pacf(diff(tecno1_ts), lag.max = 240, main = 'First differentation')
acf(diff(diff(tecno1_ts)), lag.max = 240, main = 'Second differentation')
pacf(diff(diff(tecno1_ts)), lag.max = 240, main = 'Second differentation')

De las gráficas de ACF y PACF se puede observar cómo en la segunda diferenciación se entra en la zona negativa con gran rapidez. Esto muestra cómo la serie se encuentra sobrediferenciada y, por lo tanto, es mejor optar por realizar una sola diferenciación.

Es importante resaltar que estas con conclusiones obtenidas por un análisis visual, y por eso, es importantre usar los criterios de AIC y BIC como mecanismo para decidir con certeza cual es el número óptimo de diferenciaciones que deben aplicar a la serie temporal.

A continuación se pueden visualizar las series de tiempo con las diferenciaciones de cada orden:

ts_plot(diff(tecno1_ts, lag = 1),
        title = "Close Price Series - First Differencing",
        Xtitle = "Year",
        Ytitle = "Differencing of Close Price in USD", 
        color = "#006633")
ts_plot(diff(diff(tecno1_ts, lag = 1), 12),
        title = "Close Price Series - Second Differencing",
        Xtitle = "Year",
        Ytitle = "Differencing of Close Price in USD", 
        color = "#006633")

Proceso de entrenamiento

Antes de aplicar los criterios de AIC o BIC es necesario hacer las particiones de training y testing de los datos usando la función ts_split.

ts_info(tecno1_ts)
 The tecno1_ts series is a ts object with 1 variable and 2773 observations
 Frequency: 252 
 Start time: 2012 1 
 End time: 2023 1 

A continuación se muestra la descomposición de las serie de tiempo aditivas:

ts_decompose(tecno1_ts)

Se define la partición para los diferentes horizontes que se piden en el ejercicio con sus respectivos análisis de correlación:

Horizonte de 7 días

tecno_split_7 <- ts_split(tecno1_ts, sample.out = 7)

train7 <- tecno_split_7$train
test7 <- tecno_split_7$test

par(mfrow=c(1,2))
acf(train7, lag.max = 60)
pacf(train7, lag.max = 60)

Hprizonte de 14 días

tecno_split_14 <- ts_split(tecno1_ts, sample.out = 14)

train14 <- tecno_split_14$train
test14 <- tecno_split_14$test

par(mfrow=c(1,2))
acf(train14, lag.max = 60)
pacf(train14, lag.max = 60)

Horizonte de 21 días

tecno_split_21 <- ts_split(tecno1_ts, sample.out = 21)

train21 <- tecno_split_21$train
test21 <- tecno_split_21$test

par(mfrow=c(1,2))
acf(train21, lag.max = 60)
pacf(train21, lag.max = 60)

Horizonte de 28 días

#Horizonte de 28 días
tecno_split_28 <- ts_split(tecno1_ts, sample.out = 28)

train28 <- tecno_split_28$train
test28 <- tecno_split_28$test

par(mfrow=c(1,2))
acf(train28, lag.max = 60)
pacf(train28, lag.max = 60)

En todas las gráficas ACF y PACF se puede obervar el mismo comportamiento lineal presente en la correlación vista a comienzo de este informe, los cuales reflejan el comportamiento no estacionario de la serie para cada uno de los horizontes. Por lo tanto es importante realizar es estudio de diferenciación para las series de training. Para este paso, se opta por hacer todas las pruebas para cada uno de los horizontes y posteriormente mostrar nuevamente la serie diferenciada y su correspondiente análisis de ACF y PACF.

Horizonte de 7 días

par(mfrow = c(2, 2))
acf(diff(train7, 1), lag.max = 240, main = 'First differentation')
pacf(diff(train7, 1), lag.max = 240, main = 'First differentation')
acf(diff(diff(train7, 1)), lag.max = 240, main = 'Second differentation')
pacf(diff(diff(train7, 1)), lag.max = 240, main = 'Second differentation')

Con las gráficas de autocorrelación se puede observar una mayor estabilidad la diferenciación de primer orden. Sin embargo, se realiza el test de Dickey-Fuller para probar de manera analítica que haciendo la primera diferenciación es suficiente para obtener la diferenciación de la serie.

adf.test(diff(train7, 1))

    Augmented Dickey-Fuller Test

data:  diff(train7, 1)
Dickey-Fuller = -13.372, Lag order = 14, p-value = 0.01
alternative hypothesis: stationary

Con un p-valor de 0.01 < 0.05 se rechaza la hiótesis nula de que la serie no es estacionaria, por lo que la diferenciación de primer orden es suficiente para aplicar el modelo ARIMA en la serie del precio de cierre.

A continuación la serie diferenciada para el horizonte de 7 días.

autoplot(diff(train7, 1), color = 'navy', main = 'First differentation for 7 days horizon')

Horizonte de 14 días

par(mfrow = c(2, 2))
acf(diff(train14, 1), lag.max = 240, main = 'First differentation')
pacf(diff(train14, 1), lag.max = 240, main = 'First differentation')
acf(diff(diff(train14, 1)), lag.max = 240, main = 'Second differentation')
pacf(diff(diff(train14, 1)), lag.max = 240, main = 'Second differentation')

Con las gráficas de autocorrelación se puede observar una mayor estabilidad la diferenciación de primer orden. Sin embargo, se realiza el test de Dickey-Fuller para probar de manera analítica que haciendo la primera diferenciación es suficiente para obtener la diferenciación de la serie.

adf.test(diff(train14, 1))

    Augmented Dickey-Fuller Test

data:  diff(train14, 1)
Dickey-Fuller = -13.401, Lag order = 14, p-value = 0.01
alternative hypothesis: stationary

Con un p-valor de 0.01 < 0.05 se rechaza la hiótesis nula de que la serie no es estacionaria, por lo que la diferenciación de primer orden es suficiente para aplicar el modelo ARIMA en la serie del precio de cierre.

A continuación la serie diferenciada para el horizonte de 7 días.

autoplot(diff(train14, 1), color = 'navy', main = 'First differentation for 7 days horizon')

Horizonte de 21 días

par(mfrow = c(2, 2))
acf(diff(train21, 1), lag.max = 240, main = 'First differentation')
pacf(diff(train21, 1), lag.max = 240, main = 'First differentation')
acf(diff(diff(train21, 1)), lag.max = 240, main = 'Second differentation')
pacf(diff(diff(train21, 1)), lag.max = 240, main = 'Second differentation')

Con las gráficas de autocorrelación se puede observar una mayor estabilidad la diferenciación de primer orden. Sin embargo, se realiza el test de Dickey-Fuller para probar de manera analítica que haciendo la primera diferenciación es suficiente para obtener la diferenciación de la serie.

adf.test(diff(train21, 1))

    Augmented Dickey-Fuller Test

data:  diff(train21, 1)
Dickey-Fuller = -13.431, Lag order = 14, p-value = 0.01
alternative hypothesis: stationary

Con un p-valor de 0.01 < 0.05 se rechaza la hiótesis nula de que la serie no es estacionaria, por lo que la diferenciación de primer orden es suficiente para aplicar el modelo ARIMA en la serie del precio de cierre.

A continuación la serie diferenciada para el horizonte de 7 días.

autoplot(diff(train21, 1), color = 'navy', main = 'First differentation for 7 days horizon')

Horizonte de 28 días

par(mfrow = c(2, 2))
acf(diff(train28, 1), lag.max = 240, main = 'First differentation')
pacf(diff(train28, 1), lag.max = 240, main = 'First differentation')
acf(diff(diff(train28, 1)), lag.max = 240, main = 'Second differentation')
pacf(diff(diff(train28, 1)), lag.max = 240, main = 'Second differentation')

Con las gráficas de autocorrelación se puede observar una mayor estabilidad la diferenciación de primer orden. Sin embargo, se realiza el test de Dickey-Fuller para probar de manera analítica que haciendo la primera diferenciación es suficiente para obtener la diferenciación de la serie.

adf.test(diff(train28, 1))

    Augmented Dickey-Fuller Test

data:  diff(train28, 1)
Dickey-Fuller = -14.172, Lag order = 13, p-value = 0.01
alternative hypothesis: stationary

Con un p-valor de 0.01 < 0.05 se rechaza la hiótesis nula de que la serie no es estacionaria, por lo que la diferenciación de primer orden es suficiente para aplicar el modelo ARIMA en la serie del precio de cierre.

A continuación la serie diferenciada para el horizonte de 7 días.

autoplot(diff(train28, 1), color = 'navy', main = 'First differentation for 7 days horizon')

Criterio AIC

Teniendo ya las particiones de training y tesing, se procede a calcular el mejor modelo ARIMA para la serie deiferenciada calculada anteriormente. Con este modelo se puede aplicar posteriormente entrenar los datos y con ellos, hallar las predicciones para cada uno de los horizontes. En esta primera sección se hallarán las predicciones por medio del método rolling, el cual usa las medias móviles como medio de predicción. Para esto, se utiliza la función auto.arima del paquete forecast para decidir realmente cúal es el mejor modelo para las predicciones.

auto <- auto.arima(diff(tecno1_ts, 1))
auto
Series: diff(tecno1_ts, 1) 
ARIMA(2,1,0) with drift 

Coefficients:
          ar1      ar2    drift
      -0.6736  -0.3760  -0.0006
s.e.   0.0177   0.0178   0.0056

sigma^2 = 0.3687:  log likelihood = -2548.14
AIC=5104.28   AICc=5104.29   BIC=5127.99

Del resultado de la función, se obtiene que con un AIC de 5104.29, es mejor modelo ARIMA para la serie diferenciada es ARIMA(2, 1, 0). Note que el parámetro de diferenciación coincide con el obtenido a través de los análisis gráficos y el test de Dickey-Fuller.