Carga de datos desde la API de yahoo finance

cargamos los datos de las acciones de tecnoglass del ultimo año

# simbolo de Tecnoglass -> TGLS
ticker <- "TGLS"

# Calcular la fecha de inicio y fin
end_date <- Sys.Date() # Fecha actual del sistema
start_date <- "2012-05-09" # Año de inicio de las acciones (se puede validar en la pagina de yahoo finance)

# Convertir las fechas a cadenas en el formato "yyyy-mm-dd"
end_date_str <- format(end_date, "%Y-%m-%d")

# Obtener los datos de Yahoo Finance
getSymbols(ticker, src = "yahoo", from = start_date, to = end_date_str)
## [1] "TGLS"
# Ver los datos obtenidos
print(get(ticker))
##            TGLS.Open TGLS.High TGLS.Low TGLS.Close TGLS.Volume TGLS.Adjusted
## 2012-05-10      9.97     10.00     9.50       9.80        6900      7.708232
## 2012-05-11      9.70      9.70     9.70       9.70         300      7.629573
## 2012-05-14      9.80      9.80     9.80       9.80         100      7.708232
## 2012-05-15      9.75      9.75     9.75       9.75         300      7.668901
## 2012-05-16      9.75      9.75     9.75       9.75           0      7.668901
## 2012-05-17      9.60      9.60     9.60       9.60         800      7.550916
## 2012-05-18      9.60      9.60     9.60       9.60           0      7.550916
## 2012-05-21      9.60      9.60     9.60       9.60           0      7.550916
## 2012-05-22      9.69      9.69     9.69       9.69        1500      7.621710
## 2012-05-23      9.77      9.77     9.60       9.60      153600      7.550916
##        ...                                                                  
## 2024-04-01     52.08     52.08    50.89      51.55      209300     51.549999
## 2024-04-02     50.88     51.26    50.00      50.75      296600     50.750000
## 2024-04-03     50.58     52.90    50.58      52.58      299700     52.580002
## 2024-04-04     53.00     54.32    52.29      52.46      280300     52.459999
## 2024-04-05     52.54     53.95    52.54      53.94      540700     53.939999
## 2024-04-08     53.98     55.72    53.98      55.44      666700     55.439999
## 2024-04-09     56.63     58.42    56.07      58.41      700000     58.410000
## 2024-04-10     56.88     59.03    56.37      59.00      617900     59.000000
## 2024-04-11     59.50     59.76    57.16      59.00      422500     59.000000
## 2024-04-12     58.90     58.90    57.52      57.67      311800     57.669998
close_column <- get(ticker)[, "TGLS.Close"]

Los convertimos a Dataframe

df_TGLS<- data.frame(date = index(TGLS), coredata(TGLS))

df_Close_TGLS <- df_TGLS$TGLS.Close

Analisis exploratorio de la Serie de Tiempo

Queremos vizualizar como se comportan las acciones del ultimo año de tecnoglass, para eso, usamos un Time Series plot.

ts_plot(close_column,
        title = "Value (in USD) of the last year",
        Ytitle = "Values",
        Xtitle = "Dates")

Como podemos observar para la serie de Close, la serie de tiempo a traves de los años ha seguido una tendencia al alta a partir de sus inicios, donde el año 2020 fue un punto positivo para la compañia ya que hubo un cambio significativo de tendencia. La serie de tiempo a simple vista no pareciese que tuviera estacionalidad. Vemos que en el año 2022 tuvo un comportamiento mas o menos constante alrededor de una media, y en el año 2023 hasta lo que lleva del 2024 se ha comportado de manera positiva mostrando una tendencia creciente


Ahora queremos ver un resumen de las metricas agrupadas por columnas

summary(TGLS)
##      Index              TGLS.Open       TGLS.High        TGLS.Low    
##  Min.   :2012-05-10   Min.   : 2.44   Min.   : 2.54   Min.   : 2.15  
##  1st Qu.:2015-05-05   1st Qu.: 8.83   1st Qu.: 8.98   1st Qu.: 8.69  
##  Median :2018-04-26   Median :10.20   Median :10.31   Median :10.16  
##  Mean   :2018-04-26   Mean   :14.76   Mean   :15.04   Mean   :14.48  
##  3rd Qu.:2021-04-20   3rd Qu.:14.90   3rd Qu.:15.07   3rd Qu.:14.65  
##  Max.   :2024-04-12   Max.   :59.50   Max.   :59.76   Max.   :57.52  
##    TGLS.Close      TGLS.Volume       TGLS.Adjusted   
##  Min.   : 2.290   Min.   :       0   Min.   : 2.176  
##  1st Qu.: 8.848   1st Qu.:    5800   1st Qu.: 7.559  
##  Median :10.180   Median :   30350   Median : 8.221  
##  Mean   :14.762   Mean   :  116581   Mean   :13.446  
##  3rd Qu.:14.910   3rd Qu.:  134150   3rd Qu.:11.910  
##  Max.   :59.000   Max.   :11093300   Max.   :59.000

Vemos que para la columna de Volume el maximo representa el ya comentado pico de diciembre del 2021, esto hace que la media esté muy alejada de la mediana ya que este pico por decirlo asi aleja o jala la media hacia el tercer cuartil.

Para la columna de Close, evidenciamos que la media se mantiene en 14 aunque en los ultimos 4 años la tendencia haya sido al alza. Esto es porque el valor de las acciones del 2012-2020 arrastan la media y la mediana hacia abajo. Tambien podemos decir que la razon de por qué la media esta tan cerca al tercer cuartil, es precisamente por la tendencia al alza de los ultimos 4 años. Veamoslo mejor en el boxplot

boxplot(close_column~cycle(close_column),xlab="Close", ylab = "Close value in USD" ,main ="Boxplot Close value 2012-2024")

Del mismo modo, vemos ese comportamiento reflejado en el boxplot. Acá la cantidad de datos por arriba del IQR que vemos es debido a los ultimo valores son altisimos con respecto a los anteriores que maneja esta compañia a partir del 2020, por lo que se representan como atipicos, cuando en realidad es un cambio brusco de tendencia

#options(scipen = 999)
hist(close_column, main = "Histgram for Close", xlab = "Close in USD", breaks = "Sturges", probability = TRUE)
lines(density(close_column))

Como esperabamos, la mayoria de datos se concentran entre 5 y 15 USD, ya que el valor del cierre de las acciones oscilaron en este rango por aproximadamente 8 años antes de subir

2) Rolling forecast

Entrenamiento 28 dias

Ahora estamos empleando el método de predicción conocido como rolling forecasting. Este enfoque proporciona informes que se basan en datos históricos para prever cifras futuras de manera continua a lo largo de un período determinado. Las proyecciones continuas son comúnmente utilizadas en informes financieros, gestión de la cadena de suministro, planificación y presupuesto.

El rolling forecast es una herramienta fundamental para la toma de decisiones empresariales sólidas. Gracias a su capacidad de adaptación, estas proyecciones continuas permiten a las empresas responder con mayor rapidez a los cambios del mercado. Si se aplican de manera efectiva, estas proyecciones continuas pueden ayudar a detectar áreas de bajo rendimiento, reducir los ciclos de planificación y tomar las mejores decisiones para obtener resultados óptimos. En nuestros ejemplos, estaremos previendo un día por adelantado.

Sacamos los conjuntos de datos de train y test para 28 dias.

n_TGLS <- length(df_Close_TGLS)
n_test <- 28  # Esto se puede cambiar
train_size <- n_TGLS - n_test


train <- df_Close_TGLS[1:train_size]
dates_train <- df_TGLS$date[1:train_size]
test_complete <- df_Close_TGLS[(train_size + 1):(train_size + n_test)]
dates_complete <- df_TGLS$date[(train_size + 1):(train_size + n_test)]

Sacamos los orders optimos con 28 dias

best_aic <- Inf
best_order <- NULL
best_mdl <- NULL

pq_rng <- 0:5
d_rng <- 0:3

for (i in pq_rng) {
  for (d in d_rng) {
    for (j in pq_rng) {
      tryCatch({
        # print(paste(i, d, j))
        tmp_mdl <- try(arima(train, order = c(i, d, j)), silent = TRUE)
        if (!inherits(tmp_mdl, "try-error")) {
          tmp_aic <- AIC(tmp_mdl)
          if (tmp_aic < best_aic) {
            best_aic <- tmp_aic
            best_order <- c(i, d, j)
            best_mdl <- tmp_mdl
          }
        }
      }, error = function(e) {})
    }
  }
}
## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1
cat(sprintf("aic: %6.5f | order: %s\n", best_aic, paste(best_order, collapse = ", ")))
## aic: 5339.85558 | order: 2, 1, 4
best_order1<-c(best_order)

Definimos arima rolling con 7, 14, 21 y 28 dias

Definimos la funcion arimarolling()

arima_rolling <- function(history, test, best_order,d) {
  predictions <- vector()
  c<-test$column_name[1:d]
  for (t in 1:length(c)) {
    model <- Arima(history, order=best_order1)
    model_fit <- forecast(model,d)
    yhat <- model_fit$mean[t]
    predictions <- c(predictions, yhat)
    obs <- test[t, ]
    #history <- rbind(history, obs)
    print(paste("predicted=", yhat, ", expected=", obs))
  }
  return(predictions)
}

La aplicamos

test_complete_df <- data.frame(column_name = test_complete)

yhat_7  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=7)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
yhat_14  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=14)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
## [1] "predicted= 42.9371009149202 , expected= 45.4599990844727"
## [1] "predicted= 42.7350693381808 , expected= 45.7999992370605"
## [1] "predicted= 42.6778675642528 , expected= 45.4199981689453"
## [1] "predicted= 42.8805279446935 , expected= 47.5499992370605"
## [1] "predicted= 42.8776364731191 , expected= 50.4300003051758"
## [1] "predicted= 42.6909435786998 , expected= 52.9000015258789"
## [1] "predicted= 42.745032380515 , expected= 52.2599983215332"
yhat_21  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=21)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
## [1] "predicted= 42.9371009149202 , expected= 45.4599990844727"
## [1] "predicted= 42.7350693381808 , expected= 45.7999992370605"
## [1] "predicted= 42.6778675642528 , expected= 45.4199981689453"
## [1] "predicted= 42.8805279446935 , expected= 47.5499992370605"
## [1] "predicted= 42.8776364731191 , expected= 50.4300003051758"
## [1] "predicted= 42.6909435786998 , expected= 52.9000015258789"
## [1] "predicted= 42.745032380515 , expected= 52.2599983215332"
## [1] "predicted= 42.9028537673927 , expected= 53.4700012207031"
## [1] "predicted= 42.8093512688063 , expected= 53.5999984741211"
## [1] "predicted= 42.6890942913598 , expected= 52.6800003051758"
## [1] "predicted= 42.8087150201464 , expected= 52.0299987792969"
## [1] "predicted= 42.8870269078383 , expected= 51.5499992370605"
## [1] "predicted= 42.754794519759 , expected= 50.75"
## [1] "predicted= 42.7187608578463 , expected= 52.5800018310547"
yhat_28  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=28)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
## [1] "predicted= 42.9371009149202 , expected= 45.4599990844727"
## [1] "predicted= 42.7350693381808 , expected= 45.7999992370605"
## [1] "predicted= 42.6778675642528 , expected= 45.4199981689453"
## [1] "predicted= 42.8805279446935 , expected= 47.5499992370605"
## [1] "predicted= 42.8776364731191 , expected= 50.4300003051758"
## [1] "predicted= 42.6909435786998 , expected= 52.9000015258789"
## [1] "predicted= 42.745032380515 , expected= 52.2599983215332"
## [1] "predicted= 42.9028537673927 , expected= 53.4700012207031"
## [1] "predicted= 42.8093512688063 , expected= 53.5999984741211"
## [1] "predicted= 42.6890942913598 , expected= 52.6800003051758"
## [1] "predicted= 42.8087150201464 , expected= 52.0299987792969"
## [1] "predicted= 42.8870269078383 , expected= 51.5499992370605"
## [1] "predicted= 42.754794519759 , expected= 50.75"
## [1] "predicted= 42.7187608578463 , expected= 52.5800018310547"
## [1] "predicted= 42.8510176958389 , expected= 52.4599990844727"
## [1] "predicted= 42.8479313551733 , expected= 53.939998626709"
## [1] "predicted= 42.7264252452463 , expected= 55.439998626709"
## [1] "predicted= 42.7627423894233 , expected= 58.4099998474121"
## [1] "predicted= 42.8651510654064 , expected= 59"
## [1] "predicted= 42.8033500687186 , expected= 59"
## [1] "predicted= 42.7256272457829 , expected= 57.6699981689453"

Ahora creamos la funcion para sacar el error:

forecast_accuracy <- function(forecast, actual, str_name) {
  
  mape <- mean(abs(forecast - actual)/abs(actual)) # MAPE
  mae <- mean(abs(forecast - actual))             # MAE
  rmse <- sqrt(mean((forecast - actual)^2))       # RMSE
  mse <- mean((forecast - actual)^2)              # MSE
  r2 <- cor(forecast, actual)^2                   # R2
  
  df_acc <- data.frame(MAE = mae,
                       MSE = mse,
                       MAPE = mape,
                       RMSE = rmse,
                       R2 = r2,
                       row.names = str_name)
  
  return(df_acc)
}

La aplicamos

x7<-forecast_accuracy(forecast=yhat_7,actual=test_complete[1:7], "week 1")

x14<-forecast_accuracy(forecast=yhat_14,actual=test_complete[1:14], "week 2")

x21<-forecast_accuracy(forecast=yhat_21,actual=test_complete[1:21], "week 3")

x28<-forecast_accuracy(forecast=yhat_28,actual=test_complete[1:28], "week 4")

concat<-rbind(x7,x14,x21,x28)
concat
##             MAE       MSE       MAPE     RMSE           R2
## week 1 2.082572  4.486277 0.04637260 2.118083 4.137932e-02
## week 2 3.918130 23.448831 0.08074401 4.842399 1.259897e-06
## week 3 5.806820 46.545288 0.11473252 6.822411 6.768314e-03
## week 4 7.795749 83.760292 0.14651869 9.152065 6.870161e-03

Como podemos ver el MAE aumenta a medida que aumenta la ventana de predicción, asi como el MSE y el RMSE. Notamos que el R2 esta muy alejado de ser el ideal, por lo que le falta ajuste a todos los modelos. Asi mismo, vemos que el horizonte que menos tiene error es el de 7 dias, lo cual es lógico.

Se recomienda hacer el analisis con datos despues del 2020 o 2021, ya que despues de este año tienen una tendencia mas diferente que su comportamiento en años anteriores a esta fecha. Puede ser dado por factores externos

3) solo forecast

Entrenamos el modelo 1 sola vez y pronosticamos con la ventana de 7 dias

model <- arima(train, order=best_order1)
forecast_7<-forecast(model,7)
forecast_7
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421

graficamos y vemos que siguen una tendencia uniforme, que gira alrededor de una media (aprox 42 usd)

plot_forecast(forecast_7,
              title = "Tecnoglass cierre - Forecast",
              Ytitle = "Cierre (USD)",
              Xtitle = "Tiempo")

Vemos que es la misma historia a medida que aumetan las ventanas

model <- arima(train, order=best_order1)
forecast_14<-forecast(model,14)
forecast_14
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421
## 2980       42.93710 40.86146 45.01275 39.76267 46.11153
## 2981       42.73507 40.54092 44.92922 39.37940 46.09074
## 2982       42.67787 40.37101 44.98472 39.14984 46.20590
## 2983       42.88053 40.45992 45.30114 39.17852 46.58253
## 2984       42.87764 40.35030 45.40498 39.01240 46.74287
## 2985       42.69094 40.06604 45.31585 38.67650 46.70539
## 2986       42.74503 40.02317 45.46690 38.58230 46.90777
model <- arima(train, order=best_order1)
forecast_21<-forecast(model,21)
forecast_21
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421
## 2980       42.93710 40.86146 45.01275 39.76267 46.11153
## 2981       42.73507 40.54092 44.92922 39.37940 46.09074
## 2982       42.67787 40.37101 44.98472 39.14984 46.20590
## 2983       42.88053 40.45992 45.30114 39.17852 46.58253
## 2984       42.87764 40.35030 45.40498 39.01240 46.74287
## 2985       42.69094 40.06604 45.31585 38.67650 46.70539
## 2986       42.74503 40.02317 45.46690 38.58230 46.90777
## 2987       42.90285 40.08392 45.72178 38.59167 47.21404
## 2988       42.80935 39.90005 45.71865 38.35996 47.25874
## 2989       42.68909 39.69418 45.68401 38.10877 47.26942
## 2990       42.80872 39.72703 45.89040 38.09568 47.52175
## 2991       42.88703 39.72009 46.05396 38.04362 47.73043
## 2992       42.75479 39.50820 46.00139 37.78955 47.72004
## 2993       42.71876 39.39425 46.04327 37.63437 47.80315
model <- arima(train, order=best_order1)
forecast_28<-forecast(model,28)
forecast_28
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421
## 2980       42.93710 40.86146 45.01275 39.76267 46.11153
## 2981       42.73507 40.54092 44.92922 39.37940 46.09074
## 2982       42.67787 40.37101 44.98472 39.14984 46.20590
## 2983       42.88053 40.45992 45.30114 39.17852 46.58253
## 2984       42.87764 40.35030 45.40498 39.01240 46.74287
## 2985       42.69094 40.06604 45.31585 38.67650 46.70539
## 2986       42.74503 40.02317 45.46690 38.58230 46.90777
## 2987       42.90285 40.08392 45.72178 38.59167 47.21404
## 2988       42.80935 39.90005 45.71865 38.35996 47.25874
## 2989       42.68909 39.69418 45.68401 38.10877 47.26942
## 2990       42.80872 39.72703 45.89040 38.09568 47.52175
## 2991       42.88703 39.72009 46.05396 38.04362 47.73043
## 2992       42.75479 39.50820 46.00139 37.78955 47.72004
## 2993       42.71876 39.39425 46.04327 37.63437 47.80315
## 2994       42.85102 39.44747 46.25457 37.64574 48.05629
## 2995       42.84793 39.36807 46.32780 37.52594 48.16993
## 2996       42.72643 39.17420 46.27865 37.29377 48.15908
## 2997       42.76274 39.13817 46.38732 37.21944 48.30605
## 2998       42.86515 39.16795 46.56235 37.21077 48.51953
## 2999       42.80335 39.03668 46.57002 37.04272 48.56398
## 3000       42.72563 38.89176 46.55949 36.86224 48.58902

Podemos ver que al no reentrenar el modelo en cada iteración, las estimaciones varian mucho mas con respecto al valor real, haciendo que pierdan calidad.

Evidenciamos que la mayoria oscila entre el numero 42, por lo que al graficarlo nos damos cuenta que sigue una linea recta y no alguna tendencia

plot_forecast(forecast_28,
              title = "Tecnoglass cierre - Forecast",
              Ytitle = "Cierre (USD)",
              Xtitle = "Tiempo")

4) tablas de error para los ítems 2 y 3

Para el item 2

Definimos la funcion para revisar supuestos

analyze_residuals <- function(predictions_list, actual_data,d) {
  results <- list()
  residuals_list <- list() 
  
  residuals <- actual_data - predictions_list
    
  # Prueba de normalidad (Shapiro-Wilk)
  shapiro_test <- shapiro.test(residuals)
  shapiro_pvalue <- shapiro_test$p.value
    
  # Prueba de independencia (Ljung-Box)
  # El número de lags recomendado por defecto es min(10, length(residuals) / 5)
  lb_lags <- min(10, length(residuals) / 5)
  lb_test <- Box.test(residuals, lag = lb_lags, type = "Ljung-Box")
    
    # Almacenar resultados
  results <- list(
      "Prediction Length" = length(predictions_list[[d]]),
      "Shapiro P-Value" = shapiro_pvalue,
      "Ljung-Box P-Value" = lb_test$p.value
    )
    
    # Imprimir conclusiones
  cat(paste0("Residuos para la predicción de ", d, " días:\n"))
  cat(paste0("Test de Shapiro-Wilk P-Value: ", shapiro_pvalue, "\n"))
  if (shapiro_pvalue < 0.05) {
    cat("Rechazar la hipótesis nula: Los residuos no son normales.\n")
  } else {
    cat("No rechazar la hipótesis nula: Los residuos parecen ser normales.\n")
  }
    
    cat(paste0("Test de Ljung-Box P-Value: ", lb_test$lb_pvalue[1], "\n"))
    if (lb_test$p.value < 0.05) {
      cat("Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.\n")
    } else {
      cat("No rechazar la hipótesis nula: Los residuos parecen ser independientes.\n")
    }
    
    cat("\n")
  
  return(results)
}

Consumimos la funcion para 7 dias

analyze_residuals(yhat_7,test_complete[1:7],7)
## Residuos para la predicción de 7 días:
## Test de Shapiro-Wilk P-Value: 0.810787041182961
## No rechazar la hipótesis nula: Los residuos parecen ser normales.
## Test de Ljung-Box P-Value: 
## No rechazar la hipótesis nula: Los residuos parecen ser independientes.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.810787
## 
## $`Ljung-Box P-Value`
## [1] 0.6827367

Vemos que si se puede confiar en el pronostico usando el metodo de rolling forecast, ya que vemos por las pruebas de normalidad y independencia que se cumplen los supuesto, es decir no se rechazan las hipotesis nulas. Veamos que pasa con las demas ventanas

Consumimos la funcion para 14 dias

analyze_residuals(yhat_14,test_complete[1:14],14)
## Residuos para la predicción de 14 días:
## Test de Shapiro-Wilk P-Value: 0.000894617234839748
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.0008946172
## 
## $`Ljung-Box P-Value`
## [1] 0.005858441

Evidenciamos que si aumentamos la ventana por 1 semana, ya no se puede contar con el modelo para pronosticas, dado que nos arroja una alta autocorrelación de los residuos y su falta de normalidad

Consumimos la funcion para 21 dias

analyze_residuals(yhat_21,test_complete[1:21],21)
## Residuos para la predicción de 21 días:
## Test de Shapiro-Wilk P-Value: 0.00121712931698376
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.001217129
## 
## $`Ljung-Box P-Value`
## [1] 3.894185e-10

Como era de esperarse, no se puede confiar en una ventana mucho mas grande para pronosticas

Consumimos la funcion para 28 dias

analyze_residuals(yhat_28,test_complete[1:28],28)
## Residuos para la predicción de 28 días:
## Test de Shapiro-Wilk P-Value: 0.0093674942118775
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.009367494
## 
## $`Ljung-Box P-Value`
## [1] 1.431189e-12

Vemos que entre mas alta sea la ventana, el modelo se aleja de ser consumible para el pronostico

Para el item 3

checkresiduals(forecast_7)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10

Evidenciamos en la grafica del ACF y vemos que muchos rezagos pegan en el en los bordes, lo que nos dice que hay muchos rezagos correlacionados, aunque la prueba de Ljung-Box indican que los residuos son white noise, ya que nos indica que p-value > 0.05, por lo que no tiene suficiente evidencia estadística para rechazar la hipótesis nula. Así que no puede asumir que sus valores son dependientes.

checkresiduals(forecast_14)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10
checkresiduals(forecast_21)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10
checkresiduals(forecast_28)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10

Notamos que sucede lo mismo a medida que aumentamos la ventana

5) usando BIC rolling forecast

Sacamos los conjuntos de datos de train y test para 28 dias.

n_TGLS <- length(df_Close_TGLS)
n_test <- 28  # Esto se puede cambiar
train_size <- n_TGLS - n_test


train <- df_Close_TGLS[1:train_size]
dates_train <- df_TGLS$date[1:train_size]
test_complete <- df_Close_TGLS[(train_size + 1):(train_size + n_test)]
dates_complete <- df_TGLS$date[(train_size + 1):(train_size + n_test)]

Sacamos los orders optimos con 28 dias y usando el criterio BIC

best_aic <- Inf
best_order <- NULL
best_mdl <- NULL

pq_rng <- 0:5
d_rng <- 0:3

for (i in pq_rng) {
  for (d in d_rng) {
    for (j in pq_rng) {
      tryCatch({
        # print(paste(i, d, j))
        tmp_mdl <- try(arima(train, order = c(i, d, j)), silent = TRUE)
        if (!inherits(tmp_mdl, "try-error")) {
          tmp_aic <- BIC(tmp_mdl)
          if (tmp_aic < best_aic) {
            best_aic <- tmp_aic
            best_order <- c(i, d, j)
            best_mdl <- tmp_mdl
          }
        }
      }, error = function(e) {})
    }
  }
}
## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1

## Warning in arima(train, order = c(i, d, j)): possible convergence problem:
## optim gave code = 1
cat(sprintf("BIC: %6.5f | order: %s\n", best_aic, paste(best_order, collapse = ", ")))
## BIC: 5369.86658 | order: 0, 1, 0
best_order1<-c(best_order)

Definimos la funcion arimarolling()

arima_rolling <- function(history, test, best_order,d) {
  predictions <- vector()
  c<-test$column_name[1:d]
  for (t in 1:length(c)) {
    model <- Arima(history, order=best_order1)
    model_fit <- forecast(model,d)
    yhat <- model_fit$mean[t]
    predictions <- c(predictions, yhat)
    obs <- test[t, ]
    #history <- rbind(history, obs)
    print(paste("predicted=", yhat, ", expected=", obs))
  }
  return(predictions)
}

La aplicamos

test_complete_df <- data.frame(column_name = test_complete)

yhat_7  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=7)
## [1] "predicted= 42.6800003051758 , expected= 44.25"
## [1] "predicted= 42.6800003051758 , expected= 44.5299987792969"
## [1] "predicted= 42.6800003051758 , expected= 45.0099983215332"
## [1] "predicted= 42.6800003051758 , expected= 44.8800010681152"
## [1] "predicted= 42.6800003051758 , expected= 44.6500015258789"
## [1] "predicted= 42.6800003051758 , expected= 45"
## [1] "predicted= 42.6800003051758 , expected= 45.5800018310547"
yhat_14  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=14)
## [1] "predicted= 42.6800003051758 , expected= 44.25"
## [1] "predicted= 42.6800003051758 , expected= 44.5299987792969"
## [1] "predicted= 42.6800003051758 , expected= 45.0099983215332"
## [1] "predicted= 42.6800003051758 , expected= 44.8800010681152"
## [1] "predicted= 42.6800003051758 , expected= 44.6500015258789"
## [1] "predicted= 42.6800003051758 , expected= 45"
## [1] "predicted= 42.6800003051758 , expected= 45.5800018310547"
## [1] "predicted= 42.6800003051758 , expected= 45.4599990844727"
## [1] "predicted= 42.6800003051758 , expected= 45.7999992370605"
## [1] "predicted= 42.6800003051758 , expected= 45.4199981689453"
## [1] "predicted= 42.6800003051758 , expected= 47.5499992370605"
## [1] "predicted= 42.6800003051758 , expected= 50.4300003051758"
## [1] "predicted= 42.6800003051758 , expected= 52.9000015258789"
## [1] "predicted= 42.6800003051758 , expected= 52.2599983215332"
yhat_21  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=21)
## [1] "predicted= 42.6800003051758 , expected= 44.25"
## [1] "predicted= 42.6800003051758 , expected= 44.5299987792969"
## [1] "predicted= 42.6800003051758 , expected= 45.0099983215332"
## [1] "predicted= 42.6800003051758 , expected= 44.8800010681152"
## [1] "predicted= 42.6800003051758 , expected= 44.6500015258789"
## [1] "predicted= 42.6800003051758 , expected= 45"
## [1] "predicted= 42.6800003051758 , expected= 45.5800018310547"
## [1] "predicted= 42.6800003051758 , expected= 45.4599990844727"
## [1] "predicted= 42.6800003051758 , expected= 45.7999992370605"
## [1] "predicted= 42.6800003051758 , expected= 45.4199981689453"
## [1] "predicted= 42.6800003051758 , expected= 47.5499992370605"
## [1] "predicted= 42.6800003051758 , expected= 50.4300003051758"
## [1] "predicted= 42.6800003051758 , expected= 52.9000015258789"
## [1] "predicted= 42.6800003051758 , expected= 52.2599983215332"
## [1] "predicted= 42.6800003051758 , expected= 53.4700012207031"
## [1] "predicted= 42.6800003051758 , expected= 53.5999984741211"
## [1] "predicted= 42.6800003051758 , expected= 52.6800003051758"
## [1] "predicted= 42.6800003051758 , expected= 52.0299987792969"
## [1] "predicted= 42.6800003051758 , expected= 51.5499992370605"
## [1] "predicted= 42.6800003051758 , expected= 50.75"
## [1] "predicted= 42.6800003051758 , expected= 52.5800018310547"
yhat_28  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=28)
## [1] "predicted= 42.6800003051758 , expected= 44.25"
## [1] "predicted= 42.6800003051758 , expected= 44.5299987792969"
## [1] "predicted= 42.6800003051758 , expected= 45.0099983215332"
## [1] "predicted= 42.6800003051758 , expected= 44.8800010681152"
## [1] "predicted= 42.6800003051758 , expected= 44.6500015258789"
## [1] "predicted= 42.6800003051758 , expected= 45"
## [1] "predicted= 42.6800003051758 , expected= 45.5800018310547"
## [1] "predicted= 42.6800003051758 , expected= 45.4599990844727"
## [1] "predicted= 42.6800003051758 , expected= 45.7999992370605"
## [1] "predicted= 42.6800003051758 , expected= 45.4199981689453"
## [1] "predicted= 42.6800003051758 , expected= 47.5499992370605"
## [1] "predicted= 42.6800003051758 , expected= 50.4300003051758"
## [1] "predicted= 42.6800003051758 , expected= 52.9000015258789"
## [1] "predicted= 42.6800003051758 , expected= 52.2599983215332"
## [1] "predicted= 42.6800003051758 , expected= 53.4700012207031"
## [1] "predicted= 42.6800003051758 , expected= 53.5999984741211"
## [1] "predicted= 42.6800003051758 , expected= 52.6800003051758"
## [1] "predicted= 42.6800003051758 , expected= 52.0299987792969"
## [1] "predicted= 42.6800003051758 , expected= 51.5499992370605"
## [1] "predicted= 42.6800003051758 , expected= 50.75"
## [1] "predicted= 42.6800003051758 , expected= 52.5800018310547"
## [1] "predicted= 42.6800003051758 , expected= 52.4599990844727"
## [1] "predicted= 42.6800003051758 , expected= 53.939998626709"
## [1] "predicted= 42.6800003051758 , expected= 55.439998626709"
## [1] "predicted= 42.6800003051758 , expected= 58.4099998474121"
## [1] "predicted= 42.6800003051758 , expected= 59"
## [1] "predicted= 42.6800003051758 , expected= 59"
## [1] "predicted= 42.6800003051758 , expected= 57.6699981689453"

Ahora creamos la funcion para sacar el error:

forecast_accuracy <- function(forecast, actual, str_name) {
  
  mape <- mean(abs(forecast - actual)/abs(actual)) # MAPE
  mae <- mean(abs(forecast - actual))             # MAE
  rmse <- sqrt(mean((forecast - actual)^2))       # RMSE
  mse <- mean((forecast - actual)^2)              # MSE
  r2 <- cor(forecast, actual)^2                   # R2
  
  df_acc <- data.frame(MAE = mae,
                       MSE = mse,
                       MAPE = mape,
                       RMSE = rmse,
                       R2 = r2,
                       row.names = str_name)
  
  return(df_acc)
}

La aplicamos

x7<-forecast_accuracy(forecast=yhat_7,actual=test_complete[1:7], "week 1")
## Warning in cor(forecast, actual): the standard deviation is zero
x14<-forecast_accuracy(forecast=yhat_14,actual=test_complete[1:14], "week 2")
## Warning in cor(forecast, actual): the standard deviation is zero
x21<-forecast_accuracy(forecast=yhat_21,actual=test_complete[1:21], "week 3")
## Warning in cor(forecast, actual): the standard deviation is zero
x28<-forecast_accuracy(forecast=yhat_28,actual=test_complete[1:28], "week 4")
## Warning in cor(forecast, actual): the standard deviation is zero
concat<-rbind(x7,x14,x21,x28)
concat
##             MAE      MSE       MAPE     RMSE R2
## week 1 2.162857  4.83280 0.04815886 2.198363 NA
## week 2 4.014285 24.20030 0.08280846 4.919380 NA
## week 3 5.909523 47.79566 0.11684475 6.913441 NA
## week 4 7.902142 85.49215 0.14862598 9.246196 NA

Vemos que considerando el criterio de inferencia Bayesiana (BIC) los errores cambian ligeramente

5) usando BIC solo forecast

Entrenamos el modelo 1 sola vez y pronosticamos con la ventana de 7 dias

model <- arima(train, order=best_order1)
forecast_7<-forecast(model,7)
forecast_7
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973          42.68 41.91547 43.44453 41.51076 43.84924
## 2974          42.68 41.59880 43.76120 41.02644 44.33356
## 2975          42.68 41.35580 44.00420 40.65481 44.70519
## 2976          42.68 41.15095 44.20905 40.34152 45.01848
## 2977          42.68 40.97047 44.38953 40.06550 45.29450
## 2978          42.68 40.80730 44.55270 39.81595 45.54405
## 2979          42.68 40.65725 44.70275 39.58648 45.77352

graficamos y vemos que siguen una tendencia uniforme, que gira alrededor de una media, lo que nos podria dar a entender que el criterio BIC no es tan bueno para encontrar lso orders óptimos

plot_forecast(forecast_7,
              title = "Tecnoglass cierre - Forecast",
              Ytitle = "Cierre (USD)",
              Xtitle = "Tiempo")

Vemos que es la misma historia a medida que aumetan las ventanas

model <- arima(train, order=best_order1)
forecast_14<-forecast(model,14)
forecast_14
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973          42.68 41.91547 43.44453 41.51076 43.84924
## 2974          42.68 41.59880 43.76120 41.02644 44.33356
## 2975          42.68 41.35580 44.00420 40.65481 44.70519
## 2976          42.68 41.15095 44.20905 40.34152 45.01848
## 2977          42.68 40.97047 44.38953 40.06550 45.29450
## 2978          42.68 40.80730 44.55270 39.81595 45.54405
## 2979          42.68 40.65725 44.70275 39.58648 45.77352
## 2980          42.68 40.51759 44.84241 39.37289 45.98712
## 2981          42.68 40.38642 44.97358 39.17228 46.18773
## 2982          42.68 40.26236 45.09764 38.98253 46.37747
## 2983          42.68 40.14435 45.21565 38.80206 46.55794
## 2984          42.68 40.03160 45.32840 38.62963 46.73037
## 2985          42.68 39.92346 45.43654 38.46424 46.89576
## 2986          42.68 39.81941 45.54059 38.30510 47.05490
model <- arima(train, order=best_order1)
forecast_21<-forecast(model,21)
forecast_21
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973          42.68 41.91547 43.44453 41.51076 43.84924
## 2974          42.68 41.59880 43.76120 41.02644 44.33356
## 2975          42.68 41.35580 44.00420 40.65481 44.70519
## 2976          42.68 41.15095 44.20905 40.34152 45.01848
## 2977          42.68 40.97047 44.38953 40.06550 45.29450
## 2978          42.68 40.80730 44.55270 39.81595 45.54405
## 2979          42.68 40.65725 44.70275 39.58648 45.77352
## 2980          42.68 40.51759 44.84241 39.37289 45.98712
## 2981          42.68 40.38642 44.97358 39.17228 46.18773
## 2982          42.68 40.26236 45.09764 38.98253 46.37747
## 2983          42.68 40.14435 45.21565 38.80206 46.55794
## 2984          42.68 40.03160 45.32840 38.62963 46.73037
## 2985          42.68 39.92346 45.43654 38.46424 46.89576
## 2986          42.68 39.81941 45.54059 38.30510 47.05490
## 2987          42.68 39.71900 45.64100 38.15155 47.20845
## 2988          42.68 39.62190 45.73810 38.00303 47.35697
## 2989          42.68 39.52778 45.83222 37.85909 47.50091
## 2990          42.68 39.43639 45.92361 37.71933 47.64067
## 2991          42.68 39.34751 46.01249 37.58339 47.77661
## 2992          42.68 39.26094 46.09906 37.45099 47.90901
## 2993          42.68 39.17650 46.18350 37.32186 48.03814
model <- arima(train, order=best_order1)
forecast_28<-forecast(model,28)
forecast_28
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973          42.68 41.91547 43.44453 41.51076 43.84924
## 2974          42.68 41.59880 43.76120 41.02644 44.33356
## 2975          42.68 41.35580 44.00420 40.65481 44.70519
## 2976          42.68 41.15095 44.20905 40.34152 45.01848
## 2977          42.68 40.97047 44.38953 40.06550 45.29450
## 2978          42.68 40.80730 44.55270 39.81595 45.54405
## 2979          42.68 40.65725 44.70275 39.58648 45.77352
## 2980          42.68 40.51759 44.84241 39.37289 45.98712
## 2981          42.68 40.38642 44.97358 39.17228 46.18773
## 2982          42.68 40.26236 45.09764 38.98253 46.37747
## 2983          42.68 40.14435 45.21565 38.80206 46.55794
## 2984          42.68 40.03160 45.32840 38.62963 46.73037
## 2985          42.68 39.92346 45.43654 38.46424 46.89576
## 2986          42.68 39.81941 45.54059 38.30510 47.05490
## 2987          42.68 39.71900 45.64100 38.15155 47.20845
## 2988          42.68 39.62190 45.73810 38.00303 47.35697
## 2989          42.68 39.52778 45.83222 37.85909 47.50091
## 2990          42.68 39.43639 45.92361 37.71933 47.64067
## 2991          42.68 39.34751 46.01249 37.58339 47.77661
## 2992          42.68 39.26094 46.09906 37.45099 47.90901
## 2993          42.68 39.17650 46.18350 37.32186 48.03814
## 2994          42.68 39.09406 46.26595 37.19577 48.16423
## 2995          42.68 39.01346 46.34654 37.07251 48.28749
## 2996          42.68 38.93460 46.42540 36.95191 48.40809
## 2997          42.68 38.85737 46.50263 36.83379 48.52621
## 2998          42.68 38.78167 46.57833 36.71801 48.64199
## 2999          42.68 38.70741 46.65259 36.60444 48.75556
## 3000          42.68 38.63451 46.72549 36.49295 48.86705

Evidenciamos que la mayoria oscila entre el numero 42, por lo que al graficarlo nos damos cuenta que sigue una linea recta y no alguna tendencia

plot_forecast(forecast_28,
              title = "Tecnoglass cierre - Forecast",
              Ytitle = "Cierre (USD)",
              Xtitle = "Tiempo")

tablas de error para los ítems 2 y 3 con BIC

Para el item 2

Definimos la funcion para revisar supuestos

analyze_residuals <- function(predictions_list, actual_data,d) {
  results <- list()
  residuals_list <- list() 
  
  residuals <- actual_data - predictions_list
    
  # Prueba de normalidad (Shapiro-Wilk)
  shapiro_test <- shapiro.test(residuals)
  shapiro_pvalue <- shapiro_test$p.value
    
  # Prueba de independencia (Ljung-Box)
  # El número de lags recomendado por defecto es min(10, length(residuals) / 5)
  lb_lags <- min(10, length(residuals) / 5)
  lb_test <- Box.test(residuals, lag = lb_lags, type = "Ljung-Box")
    
    # Almacenar resultados
  results <- list(
      "Prediction Length" = length(predictions_list[[d]]),
      "Shapiro P-Value" = shapiro_pvalue,
      "Ljung-Box P-Value" = lb_test$p.value
    )
    
    # Imprimir conclusiones
  cat(paste0("Residuos para la predicción de ", d, " días:\n"))
  cat(paste0("Test de Shapiro-Wilk P-Value: ", shapiro_pvalue, "\n"))
  if (shapiro_pvalue < 0.05) {
    cat("Rechazar la hipótesis nula: Los residuos no son normales.\n")
  } else {
    cat("No rechazar la hipótesis nula: Los residuos parecen ser normales.\n")
  }
    
    cat(paste0("Test de Ljung-Box P-Value: ", lb_test$lb_pvalue[1], "\n"))
    if (lb_test$p.value < 0.05) {
      cat("Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.\n")
    } else {
      cat("No rechazar la hipótesis nula: Los residuos parecen ser independientes.\n")
    }
    
    cat("\n")
  
  return(results)
}

Consumimos la funcion para 7 dias

analyze_residuals(yhat_7,test_complete[1:7],7)
## Residuos para la predicción de 7 días:
## Test de Shapiro-Wilk P-Value: 0.872707940972841
## No rechazar la hipótesis nula: Los residuos parecen ser normales.
## Test de Ljung-Box P-Value: 
## No rechazar la hipótesis nula: Los residuos parecen ser independientes.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.8727079
## 
## $`Ljung-Box P-Value`
## [1] 0.6589102

Vemos que si se puede confiar en el pronostico usando el metodo de rolling forecast, ya que vemos por las pruebas de normalidad y independencia que se cumplen los supuesto, es decir no se rechazan las hipotesis nulas. Veamos que pasa con las demas ventanas

Consumimos la funcion para 14 dias

analyze_residuals(yhat_14,test_complete[1:14],14)
## Residuos para la predicción de 14 días:
## Test de Shapiro-Wilk P-Value: 0.000982208332347104
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.0009822083
## 
## $`Ljung-Box P-Value`
## [1] 0.005578635

Evidenciamos que si aumentamos la ventana por 1 semana, ya no se puede contar con el modelo para pronosticas, dado que nos arroja una alta autocorrelación de los residuos y su falta de normalidad

Consumimos la funcion para 21 dias

analyze_residuals(yhat_21,test_complete[1:21],21)
## Residuos para la predicción de 21 días:
## Test de Shapiro-Wilk P-Value: 0.00130329398780393
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.001303294
## 
## $`Ljung-Box P-Value`
## [1] 3.983308e-10

Como era de esperarse, no se puede confiar en una ventana mucho mas grande para pronosticas

Consumimos la funcion para 28 dias

analyze_residuals(yhat_28,test_complete[1:28],28)
## Residuos para la predicción de 28 días:
## Test de Shapiro-Wilk P-Value: 0.00992973149929236
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.009929731
## 
## $`Ljung-Box P-Value`
## [1] 1.463829e-12

Vemos que entre mas alta sea la ventana, el modelo se aleja de ser consumible para el pronostico

Para el item 3

checkresiduals(forecast_7)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,1,0)
## Q* = 37.805, df = 10, p-value = 4.104e-05
## 
## Model df: 0.   Total lags used: 10

Evidenciamos en la grafica del ACF y vemos que muchos rezagos pegan en el en los bordes, lo que nos dice que hay muchos rezagos correlacionados,la prueba de Ljung-Box indican que los residuos son dependientes, ya que nos indica que p-value <= 0.05, es decir, suede rechazar la hipótesis nula asumiendo un 5% de probabilidad de error. Por lo tanto, puede asumir que sus valores muestran dependencia entre sí.

checkresiduals(forecast_14)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,1,0)
## Q* = 37.805, df = 10, p-value = 4.104e-05
## 
## Model df: 0.   Total lags used: 10
checkresiduals(forecast_21)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,1,0)
## Q* = 37.805, df = 10, p-value = 4.104e-05
## 
## Model df: 0.   Total lags used: 10
checkresiduals(forecast_28)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,1,0)
## Q* = 37.805, df = 10, p-value = 4.104e-05
## 
## Model df: 0.   Total lags used: 10

Notamos que sucede lo mismo a medida que aumentamos la ventana

5) usando HQIC rolling forecast

Sacamos los conjuntos de datos de train y test para 28 dias.

n_TGLS <- length(df_Close_TGLS)
n_test <- 28  # Esto se puede cambiar
train_size <- n_TGLS - n_test


train <- df_Close_TGLS[1:train_size]
dates_train <- df_TGLS$date[1:train_size]
test_complete <- df_Close_TGLS[(train_size + 1):(train_size + n_test)]
dates_complete <- df_TGLS$date[(train_size + 1):(train_size + n_test)]

Sacamos los orders optimos con 28 dias y usando el criterio BIC

# Inicializar el mejor HQIC con un valor grande
best_hqic <- Inf

pq_rng <- 0:4
d_rng <- 0:2

suppressWarnings(for (i in pq_rng) {
  for (d in d_rng) {
    for (j in pq_rng) {
      tryCatch({
        # print(paste(i, d, j))
        tmp_mdl <- try(arima(train, order = c(i, d, j)), silent = TRUE)
        if (!inherits(tmp_mdl, "try-error")) {
          # Calcular el número de parámetros en el modelo
          k <- length(tmp_mdl$coef)
          # Calcular AIC
          aic <- AIC(tmp_mdl)
          # Calcular el número de observaciones
          n <- length(train)
          # Calcular AICc
          tmp_hqic <- aic + 2*k*(k+1)/(n-k-1)
          if (tmp_hqic < best_hqic) {
            best_hqic <- tmp_hqic
            best_order <- c(i, d, j)
            best_mdl <- tmp_mdl
          }
        }
      }, error = function(e) {})
    }
  }
})

cat(sprintf("hqic: %6.5f | order: %s\n", best_hqic, paste(best_order, collapse = ", ")))
## hqic: 5339.88391 | order: 2, 1, 4
best_order1 <- c(best_order)

Procedemos con los mismo pasos

Definimos la funcion arimarolling()

arima_rolling <- function(history, test, best_order,d) {
  predictions <- vector()
  c<-test$column_name[1:d]
  for (t in 1:length(c)) {
    model <- Arima(history, order=best_order1)
    model_fit <- forecast(model,d)
    yhat <- model_fit$mean[t]
    predictions <- c(predictions, yhat)
    obs <- test[t, ]
    #history <- rbind(history, obs)
    print(paste("predicted=", yhat, ", expected=", obs))
  }
  return(predictions)
}

La aplicamos

test_complete_df <- data.frame(column_name = test_complete)

yhat_7  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=7)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
yhat_14  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=14)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
## [1] "predicted= 42.9371009149202 , expected= 45.4599990844727"
## [1] "predicted= 42.7350693381808 , expected= 45.7999992370605"
## [1] "predicted= 42.6778675642528 , expected= 45.4199981689453"
## [1] "predicted= 42.8805279446935 , expected= 47.5499992370605"
## [1] "predicted= 42.8776364731191 , expected= 50.4300003051758"
## [1] "predicted= 42.6909435786998 , expected= 52.9000015258789"
## [1] "predicted= 42.745032380515 , expected= 52.2599983215332"
yhat_21  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=21)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
## [1] "predicted= 42.9371009149202 , expected= 45.4599990844727"
## [1] "predicted= 42.7350693381808 , expected= 45.7999992370605"
## [1] "predicted= 42.6778675642528 , expected= 45.4199981689453"
## [1] "predicted= 42.8805279446935 , expected= 47.5499992370605"
## [1] "predicted= 42.8776364731191 , expected= 50.4300003051758"
## [1] "predicted= 42.6909435786998 , expected= 52.9000015258789"
## [1] "predicted= 42.745032380515 , expected= 52.2599983215332"
## [1] "predicted= 42.9028537673927 , expected= 53.4700012207031"
## [1] "predicted= 42.8093512688063 , expected= 53.5999984741211"
## [1] "predicted= 42.6890942913598 , expected= 52.6800003051758"
## [1] "predicted= 42.8087150201464 , expected= 52.0299987792969"
## [1] "predicted= 42.8870269078383 , expected= 51.5499992370605"
## [1] "predicted= 42.754794519759 , expected= 50.75"
## [1] "predicted= 42.7187608578463 , expected= 52.5800018310547"
yhat_28  = arima_rolling(history=train, test=test_complete_df,best_order=best_order1,d=28)
## [1] "predicted= 42.7400052900077 , expected= 44.25"
## [1] "predicted= 42.6368543800289 , expected= 44.5299987792969"
## [1] "predicted= 42.7173312260312 , expected= 45.0099983215332"
## [1] "predicted= 42.9605225224937 , expected= 44.8800010681152"
## [1] "predicted= 42.8190971779198 , expected= 44.6500015258789"
## [1] "predicted= 42.6330586825423 , expected= 45"
## [1] "predicted= 42.815130467191 , expected= 45.5800018310547"
## [1] "predicted= 42.9371009149202 , expected= 45.4599990844727"
## [1] "predicted= 42.7350693381808 , expected= 45.7999992370605"
## [1] "predicted= 42.6778675642528 , expected= 45.4199981689453"
## [1] "predicted= 42.8805279446935 , expected= 47.5499992370605"
## [1] "predicted= 42.8776364731191 , expected= 50.4300003051758"
## [1] "predicted= 42.6909435786998 , expected= 52.9000015258789"
## [1] "predicted= 42.745032380515 , expected= 52.2599983215332"
## [1] "predicted= 42.9028537673927 , expected= 53.4700012207031"
## [1] "predicted= 42.8093512688063 , expected= 53.5999984741211"
## [1] "predicted= 42.6890942913598 , expected= 52.6800003051758"
## [1] "predicted= 42.8087150201464 , expected= 52.0299987792969"
## [1] "predicted= 42.8870269078383 , expected= 51.5499992370605"
## [1] "predicted= 42.754794519759 , expected= 50.75"
## [1] "predicted= 42.7187608578463 , expected= 52.5800018310547"
## [1] "predicted= 42.8510176958389 , expected= 52.4599990844727"
## [1] "predicted= 42.8479313551733 , expected= 53.939998626709"
## [1] "predicted= 42.7264252452463 , expected= 55.439998626709"
## [1] "predicted= 42.7627423894233 , expected= 58.4099998474121"
## [1] "predicted= 42.8651510654064 , expected= 59"
## [1] "predicted= 42.8033500687186 , expected= 59"
## [1] "predicted= 42.7256272457829 , expected= 57.6699981689453"

Ahora creamos la funcion para sacar el error:

forecast_accuracy <- function(forecast, actual, str_name) {
  
  mape <- mean(abs(forecast - actual)/abs(actual)) # MAPE
  mae <- mean(abs(forecast - actual))             # MAE
  rmse <- sqrt(mean((forecast - actual)^2))       # RMSE
  mse <- mean((forecast - actual)^2)              # MSE
  r2 <- cor(forecast, actual)^2                   # R2
  
  df_acc <- data.frame(MAE = mae,
                       MSE = mse,
                       MAPE = mape,
                       RMSE = rmse,
                       R2 = r2,
                       row.names = str_name)
  
  return(df_acc)
}

La aplicamos

x7<-forecast_accuracy(forecast=yhat_7,actual=test_complete[1:7], "week 1")

x14<-forecast_accuracy(forecast=yhat_14,actual=test_complete[1:14], "week 2")

x21<-forecast_accuracy(forecast=yhat_21,actual=test_complete[1:21], "week 3")

x28<-forecast_accuracy(forecast=yhat_28,actual=test_complete[1:28], "week 4")

concat<-rbind(x7,x14,x21,x28)
concat
##             MAE       MSE       MAPE     RMSE           R2
## week 1 2.082572  4.486277 0.04637260 2.118083 4.137932e-02
## week 2 3.918130 23.448831 0.08074401 4.842399 1.259897e-06
## week 3 5.806820 46.545288 0.11473252 6.822411 6.768314e-03
## week 4 7.795749 83.760292 0.14651869 9.152065 6.870161e-03

Vemos que considerando el criterio de información de Hannan–Quinn (HQIC) los errores cambian ligeramente y los R2 dan muchisimos mas altos, se presenta un considerable sobreajuste

5) usando HQic solo forecast

Entrenamos el modelo 1 sola vez y pronosticamos con la ventana de 7 dias

model <- arima(train, order=best_order1)
forecast_7<-forecast(model,7)
forecast_7
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421

graficamos y vemos que siguen una tendencia uniforme, que gira alrededor de una media, lo que nos podria dar a entender que el criterio HQIC es mejor que el BIC pero peor que el AIC

plot_forecast(forecast_7,
              title = "Tecnoglass cierre - Forecast",
              Ytitle = "Cierre (USD)",
              Xtitle = "Tiempo")

Vemos que es la misma historia a medida que aumetan las ventanas

model <- arima(train, order=best_order1)
forecast_14<-forecast(model,14)
forecast_14
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421
## 2980       42.93710 40.86146 45.01275 39.76267 46.11153
## 2981       42.73507 40.54092 44.92922 39.37940 46.09074
## 2982       42.67787 40.37101 44.98472 39.14984 46.20590
## 2983       42.88053 40.45992 45.30114 39.17852 46.58253
## 2984       42.87764 40.35030 45.40498 39.01240 46.74287
## 2985       42.69094 40.06604 45.31585 38.67650 46.70539
## 2986       42.74503 40.02317 45.46690 38.58230 46.90777
model <- arima(train, order=best_order1)
forecast_21<-forecast(model,21)
forecast_21
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421
## 2980       42.93710 40.86146 45.01275 39.76267 46.11153
## 2981       42.73507 40.54092 44.92922 39.37940 46.09074
## 2982       42.67787 40.37101 44.98472 39.14984 46.20590
## 2983       42.88053 40.45992 45.30114 39.17852 46.58253
## 2984       42.87764 40.35030 45.40498 39.01240 46.74287
## 2985       42.69094 40.06604 45.31585 38.67650 46.70539
## 2986       42.74503 40.02317 45.46690 38.58230 46.90777
## 2987       42.90285 40.08392 45.72178 38.59167 47.21404
## 2988       42.80935 39.90005 45.71865 38.35996 47.25874
## 2989       42.68909 39.69418 45.68401 38.10877 47.26942
## 2990       42.80872 39.72703 45.89040 38.09568 47.52175
## 2991       42.88703 39.72009 46.05396 38.04362 47.73043
## 2992       42.75479 39.50820 46.00139 37.78955 47.72004
## 2993       42.71876 39.39425 46.04327 37.63437 47.80315
model <- arima(train, order=best_order1)
forecast_28<-forecast(model,28)
forecast_28
##      Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2973       42.74001 41.98012 43.49989 41.57786 43.90215
## 2974       42.63685 41.55979 43.71392 40.98963 44.28408
## 2975       42.71733 41.42229 44.01238 40.73673 44.69793
## 2976       42.96052 41.46910 44.45194 40.67960 45.24145
## 2977       42.81910 41.16358 44.47461 40.28721 45.35099
## 2978       42.63306 40.83356 44.43256 39.88096 45.38516
## 2979       42.81513 40.87376 44.75651 39.84605 45.78421
## 2980       42.93710 40.86146 45.01275 39.76267 46.11153
## 2981       42.73507 40.54092 44.92922 39.37940 46.09074
## 2982       42.67787 40.37101 44.98472 39.14984 46.20590
## 2983       42.88053 40.45992 45.30114 39.17852 46.58253
## 2984       42.87764 40.35030 45.40498 39.01240 46.74287
## 2985       42.69094 40.06604 45.31585 38.67650 46.70539
## 2986       42.74503 40.02317 45.46690 38.58230 46.90777
## 2987       42.90285 40.08392 45.72178 38.59167 47.21404
## 2988       42.80935 39.90005 45.71865 38.35996 47.25874
## 2989       42.68909 39.69418 45.68401 38.10877 47.26942
## 2990       42.80872 39.72703 45.89040 38.09568 47.52175
## 2991       42.88703 39.72009 46.05396 38.04362 47.73043
## 2992       42.75479 39.50820 46.00139 37.78955 47.72004
## 2993       42.71876 39.39425 46.04327 37.63437 47.80315
## 2994       42.85102 39.44747 46.25457 37.64574 48.05629
## 2995       42.84793 39.36807 46.32780 37.52594 48.16993
## 2996       42.72643 39.17420 46.27865 37.29377 48.15908
## 2997       42.76274 39.13817 46.38732 37.21944 48.30605
## 2998       42.86515 39.16795 46.56235 37.21077 48.51953
## 2999       42.80335 39.03668 46.57002 37.04272 48.56398
## 3000       42.72563 38.89176 46.55949 36.86224 48.58902

Evidenciamos que la mayoria oscila entre el numero 42, por lo que al graficarlo nos damos cuenta que sigue una linea recta y no alguna tendencia

plot_forecast(forecast_28,
              title = "Tecnoglass cierre - Forecast",
              Ytitle = "Cierre (USD)",
              Xtitle = "Tiempo")

tablas de error para los ítems 2 y 3 con HQIC

Para el item 2

Definimos la funcion para revisar supuestos

analyze_residuals <- function(predictions_list, actual_data,d) {
  results <- list()
  residuals_list <- list() 
  
  residuals <- actual_data - predictions_list
    
  # Prueba de normalidad (Shapiro-Wilk)
  shapiro_test <- shapiro.test(residuals)
  shapiro_pvalue <- shapiro_test$p.value
    
  # Prueba de independencia (Ljung-Box)
  # El número de lags recomendado por defecto es min(10, length(residuals) / 5)
  lb_lags <- min(10, length(residuals) / 5)
  lb_test <- Box.test(residuals, lag = lb_lags, type = "Ljung-Box")
    
    # Almacenar resultados
  results <- list(
      "Prediction Length" = length(predictions_list[[d]]),
      "Shapiro P-Value" = shapiro_pvalue,
      "Ljung-Box P-Value" = lb_test$p.value
    )
    
    # Imprimir conclusiones
  cat(paste0("Residuos para la predicción de ", d, " días:\n"))
  cat(paste0("Test de Shapiro-Wilk P-Value: ", shapiro_pvalue, "\n"))
  if (shapiro_pvalue < 0.05) {
    cat("Rechazar la hipótesis nula: Los residuos no son normales.\n")
  } else {
    cat("No rechazar la hipótesis nula: Los residuos parecen ser normales.\n")
  }
    
    cat(paste0("Test de Ljung-Box P-Value: ", lb_test$lb_pvalue[1], "\n"))
    if (lb_test$p.value < 0.05) {
      cat("Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.\n")
    } else {
      cat("No rechazar la hipótesis nula: Los residuos parecen ser independientes.\n")
    }
    
    cat("\n")
  
  return(results)
}

Consumimos la funcion para 7 dias

analyze_residuals(yhat_7,test_complete[1:7],7)
## Residuos para la predicción de 7 días:
## Test de Shapiro-Wilk P-Value: 0.810787041182961
## No rechazar la hipótesis nula: Los residuos parecen ser normales.
## Test de Ljung-Box P-Value: 
## No rechazar la hipótesis nula: Los residuos parecen ser independientes.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.810787
## 
## $`Ljung-Box P-Value`
## [1] 0.6827367

Vemos que si se puede confiar en el pronostico usando el metodo de rolling forecast, ya que vemos por las pruebas de normalidad y independencia que se cumplen los supuesto, es decir no se rechazan las hipotesis nulas. Veamos que pasa con las demas ventanas

Consumimos la funcion para 14 dias

analyze_residuals(yhat_14,test_complete[1:14],14)
## Residuos para la predicción de 14 días:
## Test de Shapiro-Wilk P-Value: 0.000894617234839748
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.0008946172
## 
## $`Ljung-Box P-Value`
## [1] 0.005858441

Evidenciamos que si aumentamos la ventana por 1 semana, ya no se puede contar con el modelo para pronosticas, dado que nos arroja una alta autocorrelación de los residuos y su falta de normalidad

Consumimos la funcion para 21 dias

analyze_residuals(yhat_21,test_complete[1:21],21)
## Residuos para la predicción de 21 días:
## Test de Shapiro-Wilk P-Value: 0.00121712931698376
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.001217129
## 
## $`Ljung-Box P-Value`
## [1] 3.894185e-10

Como era de esperarse, no se puede confiar en una ventana mucho mas grande para pronosticas

Consumimos la funcion para 28 dias

analyze_residuals(yhat_28,test_complete[1:28],28)
## Residuos para la predicción de 28 días:
## Test de Shapiro-Wilk P-Value: 0.0093674942118775
## Rechazar la hipótesis nula: Los residuos no son normales.
## Test de Ljung-Box P-Value: 
## Rechazar la hipótesis nula: Los residuos muestran autocorrelación significativa.
## $`Prediction Length`
## [1] 1
## 
## $`Shapiro P-Value`
## [1] 0.009367494
## 
## $`Ljung-Box P-Value`
## [1] 1.431189e-12

Vemos que entre mas alta sea la ventana, el modelo se aleja de ser consumible para el pronostico

Para el item 3

checkresiduals(forecast_7)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10

Evidenciamos en la grafica del ACF y vemos que muchos rezagos pegan en el en los bordes, lo que nos dice que hay muchos rezagos correlacionados, aunque la prueba de Ljung-Box indican que los residuos son white noise, ya que nos indica que p-value > 0.05, por lo que no tiene suficiente evidencia estadística para rechazar la hipótesis nula. Así que no puede asumir que sus valores son dependientes.

checkresiduals(forecast_14)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10
checkresiduals(forecast_21)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10
checkresiduals(forecast_28)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,4)
## Q* = 5.8603, df = 4, p-value = 0.2098
## 
## Model df: 6.   Total lags used: 10

Notamos que sucede lo mismo a medida que aumentamos la ventana

6) Conclusiones

Vemos que al usar los 3 criterios, si solo nos concentramos usando forecast, vemos que para el BIC es el unico best_order tal que nos da p-value <= 0.05, es decir, suede rechazar la hipótesis nula asumiendo un 5% de probabilidad de error. Por lo tanto, puede asumir que sus valores muestran dependencia entre sí.

Ahora, si nos cocentramos en el rolling forecast, vemos que siempre para las ventanas de 7 dias, usese el criterio que se use para los orders optimos (AIC, BIC ó HQIC), evidenciamos que cumple con los supuestos de normalidad e independencia, en cambio si aumentamos la brecha de dias, los supuestos se dejan de cumplir, a diferencia de cuando solo se usa forecaste. Dandonos a entender que por el metodod de rolling forecast se pueden tener modelos mas ‘robustos’ en pequeñas ventana de tiempo