library(dbplyr)
library(quantmod)
library(kableExtra)
library(TSstudio)
library(tidyverse)
library(yfR)
library(lubridate)
library(tseries)
library(plotly)
library(forecast)Para este informe, se utilizarán las siguientes librerías
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'))
figPaso 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))
autoSeries: 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.