1 Library & Data

# Install package jika belum ada
pkgs <- c("forecast", "tseries", "FinTS", "rugarch",
          "ggplot2", "dplyr", "lubridate", "nortest",
          "lmtest", "zoo", "xts")

for (p in pkgs) {
  if (!requireNamespace(p, quietly = TRUE)) install.packages(p)
}

library(forecast)
library(tseries)
library(FinTS)
library(rugarch)
library(ggplot2)
library(dplyr)
library(lubridate)
library(nortest)
library(lmtest)
library(zoo)
library(xts)
# Muat data
df <- read.csv("C:\\Users\\aspir\\Documents\\Ekolan\\Data_Historis_GOTO (1).csv", stringsAsFactors = FALSE)
df$Date  <- as.Date(df$Date)
df       <- df[order(df$Date), ]

# Gunakan harga penutupan (Close)
close_ts <- df$Close
n        <- length(close_ts)

cat("Jumlah observasi :", n, "\n")
## Jumlah observasi : 481
cat("Periode          :", format(min(df$Date)), "hingga", format(max(df$Date)), "\n")
## Periode          : 2022-04-11 hingga 2024-04-05
cat("Statistik deskriptif harga Close:\n")
## Statistik deskriptif harga Close:
summary(close_ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    56.0    88.0   112.0   157.4   218.0   404.0
ggplot(df, aes(x = Date, y = Close)) +
  geom_line(color = "#2c7bb6", linewidth = 0.7) +
  labs(title = "Harga Penutupan Saham GOTO (GOTO.JK)",
       subtitle = paste("Periode:", format(min(df$Date)), "–", format(max(df$Date))),
       x = "Tanggal", y = "Harga (IDR)") +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold"))


2 Langkah 1 — Uji Stasioneritas Mean (ADF)

Uji Augmented Dickey-Fuller (ADF) digunakan untuk memeriksa apakah deret waktu bersifat stasioner. Hipotesis nol (H₀): data tidak stasioner (terdapat unit root).

2.1 Level (Data Asli)

adf_level <- adf.test(close_ts)
print(adf_level)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  close_ts
## Dickey-Fuller = -2.2508, Lag order = 7, p-value = 0.472
## alternative hypothesis: stationary
cat("\n-- Keputusan --\n")
## 
## -- Keputusan --
if (adf_level$p.value < 0.05) {
  cat("H0 ditolak: Data STASIONER pada level (p =",
      round(adf_level$p.value, 4), ")\n")
} else {
  cat("H0 tidak ditolak: Data TIDAK STASIONER pada level (p =",
      round(adf_level$p.value, 4), ") → perlu differencing\n")
}
## H0 tidak ditolak: Data TIDAK STASIONER pada level (p = 0.472 ) → perlu differencing

2.2 First Difference (d = 1)

close_diff <- diff(close_ts, differences = 1)
adf_diff   <- adf.test(close_diff)
print(adf_diff)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  close_diff
## Dickey-Fuller = -7.1166, Lag order = 7, p-value = 0.01
## alternative hypothesis: stationary
cat("\n-- Keputusan --\n")
## 
## -- Keputusan --
if (adf_diff$p.value < 0.05) {
  cat("H0 ditolak: Data STASIONER setelah differencing 1 kali (p =",
      round(adf_diff$p.value, 4), ") → d = 1\n")
} else {
  cat("H0 tidak ditolak: Data masih TIDAK STASIONER (p =",
      round(adf_diff$p.value, 4), ") → coba d = 2\n")
}
## H0 ditolak: Data STASIONER setelah differencing 1 kali (p = 0.01 ) → d = 1
df_diff <- data.frame(
  Date  = df$Date[-1],
  Diff  = close_diff
)

ggplot(df_diff, aes(x = Date, y = Diff)) +
  geom_line(color = "#d7191c", linewidth = 0.6) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
  labs(title = "Return/Differencing Pertama Harga Penutupan GOTO",
       x = "Tanggal", y = "ΔClose (IDR)") +
  theme_minimal(base_size = 13)


3 Langkah 2 — Correlogram (ACF & PACF)

ACF (Autocorrelation Function) dan PACF (Partial Autocorrelation Function) digunakan untuk mengidentifikasi orde AR (p) dan MA (q) yang sesuai pada model ARIMA.

3.1 Data Asli

par(mfrow = c(1, 2))
acf(close_ts,  lag.max = 12, main = "ACF — Harga Close (Level)")
pacf(close_ts, lag.max = 12, main = "PACF — Harga Close (Level)")

par(mfrow = c(1, 1))

3.2 Setelah Differencing (d = 1)

par(mfrow = c(1, 2))
acf(close_diff,  lag.max = 12, main = "ACF — ΔClose (d=1)")
pacf(close_diff, lag.max = 12, main = "PACF — ΔClose (d=1)")

par(mfrow = c(1, 1))

Pembacaan: Berdasarkan pola ACF dan PACF pada data yang sudah di-differencing, kandidat model ARIMA diidentifikasi. Lag yang signifikan menunjukkan kandidat orde AR dan MA.


4 Langkah 3 — Estimasi Model ARIMA (Kandidat)

Beberapa kandidat model ARIMA diestimasi berdasarkan hasil correlogram. Parameter yang disertakan: dengan konstanta (c) dan tanpa konstanta. Model dengan MA lag ke-5 saja [5] membutuhkan parameter fixed.

# Helper untuk fit ARIMA dengan orde tertentu
y <- close_ts   # data asli; differencing ditangani oleh order d=1

# ---- Model dengan konstanta (include.constant = TRUE) ----
fit_c110 <- Arima(y, order = c(1, 1, 0), include.constant = TRUE)
fit_c011 <- Arima(y, order = c(0, 1, 1), include.constant = TRUE)
fit_c111 <- Arima(y, order = c(1, 1, 1), include.constant = TRUE)

# ARIMA(0,1,[5]) dengan konstanta: MA hanya pada lag 5
# fixed = c(MA1=0, MA2=0, MA3=0, MA4=0, MA5=NA, konstanta=NA)
fit_c015 <- Arima(y, order = c(0, 1, 5),
                  include.constant = TRUE,
                  fixed = c(0, 0, 0, 0, NA, NA))

# ARIMA(1,1,[5]) dengan konstanta: AR1 + MA hanya pada lag 5
# fixed = c(AR1=NA, MA1=0, MA2=0, MA3=0, MA4=0, MA5=NA, konstanta=NA)
fit_c115 <- Arima(y, order = c(1, 1, 5),
                  include.constant = TRUE,
                  fixed = c(NA, 0, 0, 0, 0, NA, NA))

# ---- Model tanpa konstanta ----
fit_110  <- Arima(y, order = c(1, 1, 0), include.constant = FALSE)
fit_011  <- Arima(y, order = c(0, 1, 1), include.constant = FALSE)
fit_111  <- Arima(y, order = c(1, 1, 1), include.constant = FALSE)

fit_015  <- Arima(y, order = c(0, 1, 5),
                  include.constant = FALSE,
                  fixed = c(0, 0, 0, 0, NA))

fit_115  <- Arima(y, order = c(1, 1, 5),
                  include.constant = FALSE,
                  fixed = c(NA, 0, 0, 0, 0, NA))

5 Langkah 4 — Pemilihan Model Terbaik (AIC)

model_list <- list(
  "c ARIMA(1,1,0)" = fit_c110,
  "c ARIMA(0,1,1)" = fit_c011,
  "c ARIMA(1,1,1)" = fit_c111,
  "c ARIMA(0,1,[5])" = fit_c015,
  "c ARIMA(1,1,[5])" = fit_c115,
  "ARIMA(1,1,0)"   = fit_110,
  "ARIMA(0,1,1)"   = fit_011,
  "ARIMA(1,1,1)"   = fit_111,
  "ARIMA(0,1,[5])" = fit_015,
  "ARIMA(1,1,[5])" = fit_115
)

aic_df <- data.frame(
  Model = names(model_list),
  AIC   = sapply(model_list, AIC),
  BIC   = sapply(model_list, BIC),
  LogLik = sapply(model_list, logLik)
)

aic_df <- aic_df[order(aic_df$AIC), ]
rownames(aic_df) <- NULL

# AIC per observasi (seperti pada output Excel)
aic_df$AIC_per_obs <- aic_df$AIC / n

print(aic_df)
##               Model      AIC      BIC    LogLik AIC_per_obs
## 1  c ARIMA(1,1,[5]) 3319.835 3336.530 -1655.918    6.901944
## 2    ARIMA(1,1,[5]) 3319.844 3332.366 -1656.922    6.901963
## 3    c ARIMA(1,1,0) 3322.908 3335.430 -1658.454    6.908333
## 4    c ARIMA(0,1,1) 3322.957 3335.478 -1658.478    6.908434
## 5      ARIMA(1,1,0) 3323.286 3331.634 -1659.643    6.909118
## 6      ARIMA(0,1,1) 3323.494 3331.842 -1659.747    6.909552
## 7    c ARIMA(1,1,1) 3324.862 3341.557 -1658.431    6.912395
## 8      ARIMA(1,1,1) 3325.283 3337.805 -1659.642    6.913271
## 9  c ARIMA(0,1,[5]) 3333.567 3346.088 -1663.783    6.930492
## 10   ARIMA(0,1,[5]) 3334.428 3342.776 -1665.214    6.932283
# Tandai model terbaik
best_model_name <- aic_df$Model[1]
cat("\n✔ Model terbaik berdasarkan AIC:", best_model_name, "\n")
## 
## ✔ Model terbaik berdasarkan AIC: c ARIMA(1,1,[5])
cat("  AIC =", round(aic_df$AIC[1], 6), "\n")
##   AIC = 3319.835
ggplot(aic_df, aes(x = reorder(Model, AIC), y = AIC, fill = (Model == best_model_name))) +
  geom_col(show.legend = FALSE) +
  scale_fill_manual(values = c("steelblue", "#e63946")) +
  coord_flip() +
  labs(title = "Perbandingan AIC Kandidat Model ARIMA",
       subtitle = paste("Model terbaik:", best_model_name),
       x = NULL, y = "AIC") +
  theme_minimal(base_size = 12)

# Gunakan ARIMA(1,1,[5]) dengan konstanta sebagai model terbaik (sesuai Excel)
best_fit <- fit_c115
cat("=== Ringkasan Model Terbaik: c ARIMA(1,1,[5]) ===\n")
## === Ringkasan Model Terbaik: c ARIMA(1,1,[5]) ===
summary(best_fit)
## Series: y 
## ARIMA(1,1,5) with drift 
## 
## Coefficients:
##          ar1  ma1  ma2  ma3  ma4     ma5    drift
##       0.1797    0    0    0    0  0.0981  -0.6626
## s.e.  0.0449    0    0    0    0  0.0429   0.4650
## 
## sigma^2 = 58.43:  log likelihood = -1655.92
## AIC=3319.84   AICc=3319.92   BIC=3336.53
## 
## Training set error measures:
##                       ME     RMSE      MAE        MPE     MAPE      MASE
## Training set 0.007967425 7.612315 4.740044 0.07831046 3.106406 0.9773287
##                      ACF1
## Training set 0.0004522709
coeftest(best_fit)
## 
## z test of coefficients:
## 
##        Estimate Std. Error z value Pr(>|z|)    
## ar1    0.179675   0.044917  4.0002 6.33e-05 ***
## ma5    0.098117   0.042902  2.2870  0.02219 *  
## drift -0.662595   0.464956 -1.4251  0.15414    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

6 Langkah 3 — Uji Diagnostik Residual Model Terbaik

6.1 3a. Uji Normalitas

resid_arima <- residuals(best_fit)

# Shapiro-Wilk (untuk n ≤ 5000)
if (length(resid_arima) <= 5000) {
  sw_test <- shapiro.test(resid_arima)
  cat("Shapiro-Wilk Test:\n")
  print(sw_test)
  cat("Keputusan:", ifelse(sw_test$p.value < 0.05,
      "H0 ditolak — Residual TIDAK berdistribusi normal\n",
      "H0 tidak ditolak — Residual berdistribusi normal\n"))
}
## Shapiro-Wilk Test:
## 
##  Shapiro-Wilk normality test
## 
## data:  resid_arima
## W = 0.86077, p-value < 2.2e-16
## 
## Keputusan: H0 ditolak — Residual TIDAK berdistribusi normal
# Kolmogorov-Smirnov
ks_test <- ks.test(scale(resid_arima), "pnorm")
cat("\nKolmogorov-Smirnov Test:\n")
## 
## Kolmogorov-Smirnov Test:
print(ks_test)
## 
##  Asymptotic one-sample Kolmogorov-Smirnov test
## 
## data:  scale(resid_arima)
## D = 0.11948, p-value = 2.17e-06
## alternative hypothesis: two-sided
# Jarque-Bera
jb_test <- jarque.bera.test(resid_arima)
cat("\nJarque-Bera Test:\n")
## 
## Jarque-Bera Test:
print(jb_test)
## 
##  Jarque Bera Test
## 
## data:  resid_arima
## X-squared = 1692, df = 2, p-value < 2.2e-16
cat("Keputusan:", ifelse(jb_test$p.value < 0.05,
    "H0 ditolak — Residual TIDAK normal\n",
    "H0 tidak ditolak — Residual normal\n"))
## Keputusan: H0 ditolak — Residual TIDAK normal
par(mfrow = c(1, 2))
hist(resid_arima, breaks = 30, probability = TRUE,
     main = "Histogram Residual", xlab = "Residual",
     col = "#cce5ff", border = "white")
curve(dnorm(x, mean(resid_arima), sd(resid_arima)),
      add = TRUE, col = "red", lwd = 2)
qqnorm(resid_arima, main = "Q-Q Plot Residual")
qqline(resid_arima, col = "red", lwd = 2)

par(mfrow = c(1, 1))

6.2 3b. Uji White Noise (Ljung-Box)

cat("=== Uji Ljung-Box (White Noise) ===\n\n")
## === Uji Ljung-Box (White Noise) ===
for (lag in c(1, 6, 12, 24)) {
  lb <- Box.test(resid_arima, lag = lag, type = "Ljung-Box", fitdf = 2)
  cat(sprintf("Lag %-3d: Q = %.4f, p-value = %.4f → %s\n",
              lag, lb$statistic, lb$p.value,
              ifelse(lb$p.value > 0.05, "Tidak ada autokorelasi (White Noise ✓)",
                     "Terdapat autokorelasi (bukan White Noise ✗)")))
}
## Lag 1  : Q = 0.0001, p-value = NaN → NA
## Lag 6  : Q = 3.9172, p-value = 0.4173 → Tidak ada autokorelasi (White Noise ✓)
## Lag 12 : Q = 10.0066, p-value = 0.4399 → Tidak ada autokorelasi (White Noise ✓)
## Lag 24 : Q = 32.2665, p-value = 0.0730 → Tidak ada autokorelasi (White Noise ✓)
par(mfrow = c(1, 2))
acf(resid_arima,  lag.max = 30, main = "ACF Residual")
pacf(resid_arima, lag.max = 30, main = "PACF Residual")

par(mfrow = c(1, 1))

6.3 3c. Uji Heteroskedastisitas — Efek ARCH (ARCH-LM)

Uji ARCH-LM (Lagrange Multiplier) digunakan untuk mendeteksi adanya heteroskedastisitas kondisional (efek ARCH) pada residual model ARIMA.

cat("=== Uji ARCH-LM pada Residual ARIMA ===\n\n")
## === Uji ARCH-LM pada Residual ARIMA ===
for (lag in 1:12) {
  arch_lm <- ArchTest(resid_arima, lags = lag)
  cat(sprintf("Lag %-3d: Chi² = %.4f, p-value = %.4f → %s\n",
              lag, arch_lm$statistic, arch_lm$p.value,
              ifelse(arch_lm$p.value < 0.05,
                     "Terdapat efek ARCH ✗",
                     "Tidak ada efek ARCH ✓")))
}
## Lag 1  : Chi² = 29.5696, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 2  : Chi² = 30.7980, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 3  : Chi² = 42.5202, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 4  : Chi² = 44.1316, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 5  : Chi² = 44.0574, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 6  : Chi² = 44.7274, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 7  : Chi² = 46.3282, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 8  : Chi² = 52.0703, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 9  : Chi² = 78.4336, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 10 : Chi² = 78.6379, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 11 : Chi² = 85.7399, p-value = 0.0000 → Terdapat efek ARCH ✗
## Lag 12 : Chi² = 87.5614, p-value = 0.0000 → Terdapat efek ARCH ✗

7 Langkah 5 — Pemodelan ARCH

Karena terdapat efek ARCH pada residual, dilakukan pemodelan volatilitas menggunakan model ARCH. Beberapa orde ARCH diuji untuk mendapatkan model terbaik.

# Gunakan paket rugarch untuk estimasi ARCH(q)
# Distribusi: normal; mean model: ARFIMA dengan urutan ARIMA(1,1,[5])

aic_arch <- c()

for (q in 1:5) {
  spec <- ugarchspec(
    variance.model  = list(model = "sGARCH", garchOrder = c(q, 0)),
    mean.model      = list(armaOrder = c(1, 5),
                           include.mean = TRUE,
                           external.regressors = NULL),
    distribution.model = "norm"
  )

  tryCatch({
    fit_arch <- ugarchfit(spec = spec, data = close_ts,
                          solver = "hybrid", out.sample = 0)
    ic       <- infocriteria(fit_arch)
    aic_val  <- ic["Akaike", ]
    aic_arch[q] <- aic_val
    cat(sprintf("ARCH(%d): AIC = %.6f\n", q, aic_val))
  }, error = function(e) {
    cat(sprintf("ARCH(%d): Gagal diestimasi — %s\n", q, e$message))
    aic_arch[q] <<- NA
  })
}
## ARCH(1): AIC = 6.656117
## ARCH(2): AIC = 6.650132
## ARCH(3): AIC = 6.491010
## ARCH(4): AIC = 6.492193
## ARCH(5): AIC = 6.458745
best_q <- which.min(aic_arch)
cat("\n✔ Model ARCH terbaik berdasarkan AIC: ARCH(", best_q, ")\n", sep = "")
## 
## ✔ Model ARCH terbaik berdasarkan AIC: ARCH(5)
cat("  AIC =", round(aic_arch[best_q], 6), "\n")
##   AIC = 6.458745
arch_df <- data.frame(
  Orde = paste0("ARCH(", 1:length(aic_arch), ")"),
  AIC  = aic_arch
)

ggplot(arch_df[!is.na(arch_df$AIC), ], aes(x = Orde, y = AIC,
       fill = (Orde == paste0("ARCH(", best_q, ")")))) +
  geom_col(show.legend = FALSE) +
  scale_fill_manual(values = c("steelblue", "#e63946")) +
  labs(title = "Perbandingan AIC Model ARCH",
       subtitle = paste("Model terbaik: ARCH(", best_q, ")"),
       x = NULL, y = "AIC") +
  theme_minimal(base_size = 12)

7.1 Estimasi Model Terbaik: ARCH(5)

spec_best <- ugarchspec(
  variance.model     = list(model = "sGARCH", garchOrder = c(5, 0)),
  mean.model         = list(armaOrder = c(1, 5), include.mean = TRUE),
  distribution.model = "norm"
)

fit_best_arch <- ugarchfit(spec = spec_best, data = close_ts,
                           solver = "hybrid")
show(fit_best_arch)
## 
## *---------------------------------*
## *          GARCH Model Fit        *
## *---------------------------------*
## 
## Conditional Variance Dynamics    
## -----------------------------------
## GARCH Model  : sGARCH(5,0)
## Mean Model   : ARFIMA(1,0,5)
## Distribution : norm 
## 
## Optimal Parameters
## ------------------------------------
##          Estimate  Std. Error     t value Pr(>|t|)
## mu     367.301942    5.031746   72.996920 0.000000
## ar1      0.999747    0.000716 1396.795089 0.000000
## ma1      0.049272    0.059078    0.834018 0.404271
## ma2     -0.082375    0.042941   -1.918310 0.055072
## ma3      0.036169    0.052152    0.693523 0.487981
## ma4     -0.090751    0.034375   -2.640051 0.008289
## ma5      0.007451    0.037432    0.199052 0.842222
## omega    7.670257    1.591272    4.820205 0.000001
## alpha1   0.257641    0.060126    4.285025 0.000018
## alpha2   0.051024    0.043601    1.170242 0.241903
## alpha3   0.554638    0.087555    6.334743 0.000000
## alpha4   0.000000    0.032782    0.000009 0.999992
## alpha5   0.135696    0.047707    2.844375 0.004450
## 
## Robust Standard Errors:
##          Estimate  Std. Error    t value Pr(>|t|)
## mu     367.301942    6.134227  59.877465 0.000000
## ar1      0.999747    0.001046 955.954714 0.000000
## ma1      0.049272    0.096327   0.511507 0.608996
## ma2     -0.082375    0.057451  -1.433829 0.151621
## ma3      0.036169    0.078311   0.461860 0.644182
## ma4     -0.090751    0.037702  -2.407074 0.016081
## ma5      0.007451    0.048946   0.152229 0.879006
## omega    7.670257    2.413212   3.178443 0.001481
## alpha1   0.257641    0.083547   3.083792 0.002044
## alpha2   0.051024    0.060078   0.849290 0.395720
## alpha3   0.554638    0.192897   2.875314 0.004036
## alpha4   0.000000    0.040231   0.000008 0.999994
## alpha5   0.135696    0.078533   1.727891 0.084008
## 
## LogLikelihood : -1540.328 
## 
## Information Criteria
## ------------------------------------
##                    
## Akaike       6.4587
## Bayes        6.5716
## Shibata      6.4573
## Hannan-Quinn 6.5031
## 
## Weighted Ljung-Box Test on Standardized Residuals
## ------------------------------------
##                          statistic   p-value
## Lag[1]                      0.1774 6.736e-01
## Lag[2*(p+q)+(p+q)-1][17]   11.7706 9.447e-06
## Lag[4*(p+q)+(p+q)-1][29]   16.7253 2.613e-01
## d.o.f=6
## H0 : No serial correlation
## 
## Weighted Ljung-Box Test on Standardized Squared Residuals
## ------------------------------------
##                          statistic p-value
## Lag[1]                      0.1817  0.6699
## Lag[2*(p+q)+(p+q)-1][14]    3.0796  0.9511
## Lag[4*(p+q)+(p+q)-1][24]    6.5977  0.9490
## d.o.f=5
## 
## Weighted ARCH LM Tests
## ------------------------------------
##              Statistic Shape Scale P-Value
## ARCH Lag[6]     0.1416 0.500 2.000  0.7067
## ARCH Lag[8]     0.5766 1.480 1.774  0.8804
## ARCH Lag[10]    1.4806 2.424 1.650  0.8642
## 
## Nyblom stability test
## ------------------------------------
## Joint Statistic:  5.5301
## Individual Statistics:               
## mu     0.005307
## ar1    0.047884
## ma1    0.471348
## ma2    0.504265
## ma3    0.033829
## ma4    0.155076
## ma5    0.409911
## omega  0.991344
## alpha1 1.176312
## alpha2 0.362509
## alpha3 0.522782
## alpha4 0.277211
## alpha5 0.984235
## 
## Asymptotic Critical Values (10% 5% 1%)
## Joint Statistic:          2.89 3.15 3.69
## Individual Statistic:     0.35 0.47 0.75
## 
## Sign Bias Test
## ------------------------------------
##                    t-value   prob sig
## Sign Bias            1.407 0.1602    
## Negative Sign Bias   1.129 0.2596    
## Positive Sign Bias   1.351 0.1772    
## Joint Effect         3.244 0.3555    
## 
## 
## Adjusted Pearson Goodness-of-Fit Test:
## ------------------------------------
##   group statistic p-value(g-1)
## 1    20     51.27    8.514e-05
## 2    30     71.79    1.721e-05
## 3    40     84.24    3.602e-05
## 4    50    106.01    4.424e-06
## 
## 
## Elapsed time : 0.7286229

7.2 Uji Signifikansi Parameter ARCH(5)

coef_arch <- coef(fit_best_arch)
se_arch   <- sqrt(diag(vcov(fit_best_arch)))
z_arch    <- coef_arch / se_arch
p_arch    <- 2 * pnorm(abs(z_arch), lower.tail = FALSE)

sig_df <- data.frame(
  Parameter = names(coef_arch),
  Estimasi  = round(coef_arch, 6),
  Std_Error = round(se_arch, 6),
  z_value   = round(z_arch, 4),
  p_value   = round(p_arch, 4),
  Signifikan = ifelse(p_arch < 0.05, "Ya ✓", "Tidak ✗")
)
print(sig_df)
##        Parameter   Estimasi Std_Error   z_value p_value Signifikan
## mu            mu 367.301942  5.031746   72.9969  0.0000       Ya ✓
## ar1          ar1   0.999747  0.000716 1396.7951  0.0000       Ya ✓
## ma1          ma1   0.049272  0.059078    0.8340  0.4043    Tidak ✗
## ma2          ma2  -0.082375  0.042941   -1.9183  0.0551    Tidak ✗
## ma3          ma3   0.036169  0.052152    0.6935  0.4880    Tidak ✗
## ma4          ma4  -0.090751  0.034375   -2.6401  0.0083       Ya ✓
## ma5          ma5   0.007451  0.037432    0.1991  0.8422    Tidak ✗
## omega      omega   7.670257  1.591272    4.8202  0.0000       Ya ✓
## alpha1    alpha1   0.257641  0.060126    4.2850  0.0000       Ya ✓
## alpha2    alpha2   0.051024  0.043601    1.1702  0.2419    Tidak ✗
## alpha3    alpha3   0.554638  0.087555    6.3347  0.0000       Ya ✓
## alpha4    alpha4   0.000000  0.032782    0.0000  1.0000    Tidak ✗
## alpha5    alpha5   0.135696  0.047707    2.8444  0.0044       Ya ✓

7.3 Uji ARCH-LM pada Residual ARCH(3)

resid_arch <- residuals(fit_best_arch, standardize = TRUE)

cat("=== Uji ARCH-LM pada Residual Model ARCH(3) ===\n\n")
## === Uji ARCH-LM pada Residual Model ARCH(3) ===
for (lag in c(1, 5, 10, 15)) {
  arch_lm2 <- ArchTest(resid_arch, lags = lag)
  cat(sprintf("Lag %-3d: Chi² = %.4f, p-value = %.4f → %s\n",
              lag, arch_lm2$statistic, arch_lm2$p.value,
              ifelse(arch_lm2$p.value > 0.05,
                     "Tidak ada efek ARCH (model cukup ✓)",
                     "Masih terdapat efek ARCH ✗")))
}
## Lag 1  : Chi² = 0.1806, p-value = 0.6708 → Tidak ada efek ARCH (model cukup ✓)
## Lag 5  : Chi² = 2.0143, p-value = 0.8472 → Tidak ada efek ARCH (model cukup ✓)
## Lag 10 : Chi² = 4.2293, p-value = 0.9364 → Tidak ada efek ARCH (model cukup ✓)
## Lag 15 : Chi² = 9.0539, p-value = 0.8747 → Tidak ada efek ARCH (model cukup ✓)
sigma_arch <- sigma(fit_best_arch)

vol_df <- data.frame(
  Date  = df$Date,
  Close = close_ts,
  Sigma = sigma_arch
)

p1 <- ggplot(vol_df, aes(x = Date, y = Close)) +
  geom_line(color = "#2c7bb6") +
  labs(title = "Harga Penutupan GOTO", x = NULL, y = "Harga (IDR)") +
  theme_minimal(base_size = 11)

p2 <- ggplot(vol_df, aes(x = Date, y = Sigma)) +
  geom_line(color = "#e63946") +
  labs(title = "Estimasi Volatilitas Kondisional — ARCH(3)",
       x = "Tanggal", y = "σ_t (Std Dev Kondisional)") +
  theme_minimal(base_size = 11)

gridExtra::grid.arrange(p1, p2, ncol = 1)


8 Langkah 6 — Forecasting

Dilakukan peramalan harga saham GOTO menggunakan model terbaik ARIMA(1,1,[5])-ARCH(5) untuk 30 hari ke depan.

8.1 Forecast Mean (ARIMA)

h <- 30  # horizon peramalan (hari kerja)

fc_arima <- forecast(best_fit, h = h, level = c(80, 95))
autoplot(fc_arima) +
  labs(title = "Forecast Harga GOTO — ARIMA(1,1,[5]) dengan Konstanta",
       subtitle = paste("Horizon:", h, "hari ke depan"),
       x = "Waktu", y = "Harga (IDR)") +
  theme_minimal(base_size = 12)

tanggal_fc <- seq(max(df$Date) + 1, by = "day", length.out = h * 2)
tanggal_fc <- tanggal_fc[!weekdays(tanggal_fc) %in% c("Saturday", "Sunday")][1:h]

fc_df <- data.frame(
  Tanggal    = tanggal_fc,
  Forecast   = round(as.numeric(fc_arima$mean), 2),
  Lower_80   = round(as.numeric(fc_arima$lower[, 1]), 2),
  Upper_80   = round(as.numeric(fc_arima$upper[, 1]), 2),
  Lower_95   = round(as.numeric(fc_arima$lower[, 2]), 2),
  Upper_95   = round(as.numeric(fc_arima$upper[, 2]), 2)
)

print(fc_df)
##       Tanggal Forecast Lower_80 Upper_80 Lower_95 Upper_95
## 1  2024-04-08    67.17    57.37    76.96    52.19    82.15
## 2  2024-04-09    66.74    51.59    81.89    43.57    89.91
## 3  2024-04-10    65.94    46.69    85.18    36.50    95.37
## 4  2024-04-11    65.44    42.80    88.09    30.81   100.08
## 5  2024-04-12    64.71    39.11    90.31    25.56   103.87
## 6  2024-04-15    64.04    35.37    92.71    20.20   107.88
## 7  2024-04-16    63.38    31.87    94.88    15.19   111.56
## 8  2024-04-17    62.71    28.59    96.84    10.52   114.90
## 9  2024-04-18    62.05    25.49    98.61     6.14   117.96
## 10 2024-04-19    61.39    22.55   100.23     1.99   120.79
## 11 2024-04-22    60.72    19.73   101.72    -1.97   123.42
## 12 2024-04-23    60.06    17.02   103.10    -5.76   125.89
## 13 2024-04-24    59.40    14.41   104.39    -9.41   128.21
## 14 2024-04-25    58.74    11.87   105.60   -12.94   130.41
## 15 2024-04-26    58.07     9.41   106.74   -16.35   132.50
## 16 2024-04-29    57.41     7.01   107.81   -19.67   134.49
## 17 2024-04-30    56.75     4.67   108.83   -22.90   136.40
## 18 2024-05-01    56.09     2.38   109.79   -26.05   138.22
## 19 2024-05-02    55.42     0.14   110.71   -29.13   139.97
## 20 2024-05-03    54.76    -2.06   111.58   -32.13   141.66
## 21 2024-05-06    54.10    -4.21   112.41   -35.08   143.28
## 22 2024-05-07    53.44    -6.33   113.20   -37.97   144.84
## 23 2024-05-08    52.77    -8.42   113.96   -40.81   146.35
## 24 2024-05-09    52.11   -10.47   114.69   -43.60   147.82
## 25 2024-05-10    51.45   -12.49   115.39   -46.34   149.23
## 26 2024-05-13    50.79   -14.48   116.05   -49.04   150.61
## 27 2024-05-14    50.12   -16.45   116.70   -51.69   151.94
## 28 2024-05-15    49.46   -18.39   117.31   -54.31   153.23
## 29 2024-05-16    48.80   -20.31   117.91   -56.89   154.49
## 30 2024-05-17    48.13   -22.21   118.48   -59.44   155.71

8.2 Forecast Volatilitas (ARCH)

fc_arch <- ugarchforecast(fit_best_arch, n.ahead = h)

sigma_fc <- as.numeric(sigma(fc_arch))
mean_fc  <- as.numeric(fitted(fc_arch))

fc_vol_df <- data.frame(
  Hari        = 1:h,
  Tanggal     = tanggal_fc,
  Mean_FC     = round(mean_fc, 4),
  Sigma_FC    = round(sigma_fc, 4),
  Lower_95    = round(mean_fc - 1.96 * sigma_fc, 4),
  Upper_95    = round(mean_fc + 1.96 * sigma_fc, 4)
)

print(fc_vol_df)
##    Hari    Tanggal Mean_FC Sigma_FC Lower_95 Upper_95
## 1     1 2024-04-08 67.5617   3.5133  60.6757  74.4477
## 2     2 2024-04-09 68.0746   3.6094  61.0002  75.1491
## 3     3 2024-04-10 67.9312   3.7351  60.6104  75.2520
## 4     4 2024-04-11 68.1627   4.3714  59.5947  76.7307
## 5     5 2024-04-12 68.2264   4.5688  59.2716  77.1813
## 6     6 2024-04-15 68.3020   4.8411  58.8135  77.7905
## 7     7 2024-04-16 68.3775   5.2096  58.1666  78.5883
## 8     8 2024-04-17 68.4530   5.4156  57.8383  79.0676
## 9     9 2024-04-18 68.5285   5.6748  57.4059  79.6510
## 10   10 2024-04-19 68.6039   5.9455  56.9507  80.2571
## 11   11 2024-04-22 68.6794   6.1537  56.6181  80.7406
## 12   12 2024-04-23 68.7548   6.3855  56.2393  81.2703
## 13   13 2024-04-24 68.8302   6.6101  55.8744  81.7860
## 14   14 2024-04-25 68.9056   6.8103  55.5573  82.2538
## 15   15 2024-04-26 68.9809   7.0186  55.2245  82.7374
## 16   16 2024-04-29 69.0563   7.2181  54.9088  83.2037
## 17   17 2024-04-30 69.1316   7.4071  54.6138  83.6494
## 18   18 2024-05-01 69.2069   7.5970  54.3167  84.0971
## 19   19 2024-05-02 69.2822   7.7801  54.0332  84.5312
## 20   20 2024-05-03 69.3575   7.9577  53.7604  84.9545
## 21   21 2024-05-06 69.4327   8.1336  53.4910  85.3745
## 22   22 2024-05-07 69.5079   8.3044  53.2314  85.7845
## 23   23 2024-05-08 69.5832   8.4716  52.9789  86.1874
## 24   24 2024-05-09 69.6584   8.6363  52.7313  86.5854
## 25   25 2024-05-10 69.7335   8.7972  52.4910  86.9760
## 26   26 2024-05-13 69.8087   8.9553  52.2564  87.3610
## 27   27 2024-05-14 69.8838   9.1108  52.0267  87.7410
## 28   28 2024-05-15 69.9589   9.2634  51.8027  88.1152
## 29   29 2024-05-16 70.0340   9.4136  51.5835  88.4846
## 30   30 2024-05-17 70.1091   9.5614  51.3688  88.8494
# Gabungkan data historis dan forecast
hist_df <- data.frame(
  Tanggal = df$Date,
  Close   = close_ts,
  Type    = "Aktual"
)

fc_plot_df <- data.frame(
  Tanggal   = tanggal_fc,
  Mean      = mean_fc,
  Lower_95  = mean_fc - 1.96 * sigma_fc,
  Upper_95  = mean_fc + 1.96 * sigma_fc
)

# Ambil 60 hari terakhir data historis untuk visualisasi
hist_recent <- tail(hist_df, 60)

ggplot() +
  geom_line(data = hist_recent, aes(x = Tanggal, y = Close),
            color = "#2c7bb6", linewidth = 0.8) +
  geom_ribbon(data = fc_plot_df,
              aes(x = Tanggal, ymin = Lower_95, ymax = Upper_95),
              fill = "#e63946", alpha = 0.2) +
  geom_line(data = fc_plot_df, aes(x = Tanggal, y = Mean),
            color = "#e63946", linewidth = 0.9, linetype = "dashed") +
  geom_vline(xintercept = as.numeric(max(df$Date)),
             linetype = "dotted", color = "gray50") +
  annotate("text", x = max(df$Date) + 2, y = max(hist_recent$Close),
           label = "← Forecast", hjust = 0, color = "#e63946", size = 3.5) +
  labs(title = "Forecast Harga Penutupan GOTO — ARIMA(1,1,[5])-ARCH(3)",
       subtitle = paste("Horizon:", h, "hari kerja | Pita: 95% CI"),
       x = "Tanggal", y = "Harga (IDR)") +
  theme_minimal(base_size = 12)


9 Ringkasan Hasil Analisis

cat("============================================================\n")
## ============================================================
cat("  RINGKASAN ANALISIS DERET WAKTU SAHAM GOTO\n")
##   RINGKASAN ANALISIS DERET WAKTU SAHAM GOTO
cat("============================================================\n\n")
## ============================================================
cat("[1] Stasioneritas:\n")
## [1] Stasioneritas:
cat("    • Data level tidak stasioner → differencing 1 kali (d = 1)\n")
##     • Data level tidak stasioner → differencing 1 kali (d = 1)
cat("    • ADF setelah differencing: p =",
    round(adf_diff$p.value, 4), "→ stasioner\n\n")
##     • ADF setelah differencing: p = 0.01 → stasioner
cat("[2] Model ARIMA Terbaik: c ARIMA(1,1,[5])\n")
## [2] Model ARIMA Terbaik: c ARIMA(1,1,[5])
cat("    • AIC terkecil di antara semua kandidat model\n")
##     • AIC terkecil di antara semua kandidat model
cat("    • Komponen: AR(1) + MA(5) + konstanta + differencing 1\n\n")
##     • Komponen: AR(1) + MA(5) + konstanta + differencing 1
cat("[3] Diagnostik Residual:\n")
## [3] Diagnostik Residual:
cat("    • Uji White Noise (Ljung-Box): lihat output Bagian 3b\n")
##     • Uji White Noise (Ljung-Box): lihat output Bagian 3b
cat("    • Uji Normalitas (JB): lihat output Bagian 3a\n")
##     • Uji Normalitas (JB): lihat output Bagian 3a
cat("    • Efek ARCH terdeteksi → perlu model ARCH\n\n")
##     • Efek ARCH terdeteksi → perlu model ARCH
cat("[4] Model ARCH Terbaik: ARCH(5)\n")
## [4] Model ARCH Terbaik: ARCH(5)
cat("    • Dipilih berdasarkan AIC terkecil di antara ARCH(1)–ARCH(5)\n")
##     • Dipilih berdasarkan AIC terkecil di antara ARCH(1)–ARCH(5)
cat("      yang parameternya signifikan\n")
##       yang parameternya signifikan
cat("    • Residual ARCH(5) tidak mengandung efek ARCH lagi\n\n")
##     • Residual ARCH(5) tidak mengandung efek ARCH lagi
cat("[5] Forecast:", h, "hari ke depan\n")
## [5] Forecast: 30 hari ke depan
cat("    • Menggunakan model gabungan ARIMA(1,1,[5])-ARCH(3)\n")
##     • Menggunakan model gabungan ARIMA(1,1,[5])-ARCH(3)
cat("    • Interval kepercayaan 95% disertakan\n")
##     • Interval kepercayaan 95% disertakan
cat("============================================================\n")
## ============================================================

Analisis dilakukan dengan R. Paket utama: forecast, tseries, rugarch, FinTS.