Pendahuluan

Latar Belakang

Investasi merupakan komitmen untuk mengalokasikan dana saat ini guna memperoleh keuntungan di masa depan, dengan memperhitungkan periode waktu investasi, tingkat inflasi, dan ketidakpastian arus kas [@Reilly2019]. Salah satu instrumen investasi yang populer adalah saham, di mana volatilitas harga menjadi perhatian utama investor.

PT GoTo Gojek Tokopedia Tbk (GOTO) merupakan salah satu emiten dengan volatilitas yang sangat tinggi. Sejak IPO pada 11 April 2022 di harga Rp 338/lembar, harga saham GOTO mengalami penurunan drastis hingga 68,5%. Merger dengan TikTok yang awalnya diharapkan menjadi sentimen positif justru mendorong harga turun lebih jauh sekitar 20,37% menuju Rp 75/lembar pada tahun 2024.

Tujuan Analisis

Analisis ini bertujuan untuk:

  1. Mengidentifikasi karakteristik volatilitas harga saham GOTO
  2. Membangun model ARIMA terbaik sebagai persamaan mean
  3. Mendeteksi dan memodelkan efek heteroskedastisitas bersyarat menggunakan model ARCH
  4. Melakukan peramalan volatilitas saham GOTO ke depan

Metodologi

Analisis dilakukan mengikuti 12 tahap sistematis:

No. Tahap Keterangan
1 Import Data Data harga penutupan harian GOTO
2 Visualisasi Plot time series dan eksplorasi data
3 Uji Stasioneritas Augmented Dickey-Fuller (ADF) Test
4 Identifikasi Model Mean Analisis ACF dan PACF
5 Seleksi Model ARIMA Uji kandidat model satu per satu
6 Uji Residual White noise test (Ljung-Box)
7 Uji Efek ARCH ARCH-LM Test
8 Penentuan Orde ARCH(p) ACF residual kuadrat + perbandingan AIC
9 Estimasi Model ARCH Fitting ARCH(p) terpilih
10 Evaluasi Model Signifikansi, AIC/BIC, diagnostic plot
11 Interpretasi Makna ekonomi dari parameter
12 Forecasting Peramalan volatilitas ke depan

Tahap 1: Import dan Eksplorasi Data

Import Data

# !! SESUAIKAN PATH INI DENGAN LOKASI FILE DI KOMPUTER ANDA !!
file_path <- "C:\\Users\\aspir\\Documents\\Ekolan\\Data_Historis_GOTO (1).csv"

data_goto <- read.csv(file_path)
data_goto$Date <- as.Date(substr(data_goto$Date, 1, 10))
harga <- data_goto$Close

cat("Periode data :", format(min(data_goto$Date)), "s.d.",
    format(max(data_goto$Date)), "\n")
#> Periode data : 2022-04-11 s.d. 2024-04-05
cat("Total observasi:", length(harga), "\n")
#> Total observasi: 481

Statistik Deskriptif

# Hitung statistik deskriptif
stat_desk <- data.frame(
  Statistik = c("N (Observasi)", "Minimum (Rp)", "Maksimum (Rp)",
                "Mean (Rp)", "Median (Rp)", "Std. Deviasi",
                "Skewness", "Kurtosis"),
  Nilai = c(
    length(harga),
    min(harga,         na.rm = TRUE),
    max(harga,         na.rm = TRUE),
    round(mean(harga,  na.rm = TRUE), 2),
    round(median(harga,na.rm = TRUE), 2),
    round(sd(harga,    na.rm = TRUE), 2),
    round(skewness(harga, na.rm = TRUE), 4),
    round(kurtosis(harga, na.rm = TRUE), 4)
  )
)

kable(stat_desk,
      caption = "Statistik Deskriptif Harga Penutupan Saham GOTO",
      booktabs = TRUE, align = c("l", "r")) |>
  kable_styling(latex_options = c("hold_position", "striped"),
                full_width = FALSE)
Statistik Deskriptif Harga Penutupan Saham GOTO
Statistik Nilai
N (Observasi) 481.0000
Minimum (Rp) 56.0000
Maksimum (Rp) 404.0000
Mean (Rp) 157.4400
Median (Rp) 112.0000
Std. Deviasi 96.3000
Skewness 1.0857
Kurtosis 2.7468

Interpretasi:

  • Data terdiri dari 481 observasi harga penutupan harian GOTO
  • Harga minimum Rp 56 dan maksimum Rp 404 dengan rata-rata Rp 157.44, menunjukkan rentang fluktuasi yang sangat besar
  • Skewness 1.0857 > 0 → distribusi menceng ke kanan (right-skewed), artinya lebih banyak periode dengan harga rendah dan beberapa lonjakan harga tinggi
  • Kurtosis 2.7468 > 3 → distribusi leptokurtic (ekor lebih tebal dari normal), yang merupakan ciri khas data saham dengan extreme events yang lebih sering dari distribusi normal

Tahap 2: Visualisasi Data Time Series

ggplot(data_goto, aes(x = Date, y = Close)) +
  geom_line(color = "navy", linewidth = 0.6) +
  labs(
    title    = "Harga Penutupan Saham GOTO",
    subtitle = paste(format(min(data_goto$Date)), "–", format(max(data_goto$Date))),
    x        = "Tanggal",
    y        = "Harga Penutupan (Rp)"
  ) +
  theme_bw(base_size = 10) +
  theme(plot.title = element_text(face = "bold"))
Pergerakan Harga Penutupan Saham GOTO (Level)

Pergerakan Harga Penutupan Saham GOTO (Level)

Interpretasi:

Plot di atas memperlihatkan bahwa harga saham GOTO menunjukkan tren penurunan yang kuat sejak IPO. Pergerakan harga tidak berfluktuasi di sekitar nilai rata-rata yang konstan (tidak mean-reverting), melainkan terus bergerak turun. Pola ini adalah indikasi kuat bahwa data tidak stasioner pada level, sehingga perlu dilakukan differencing. Selain itu, terlihat beberapa periode dengan fluktuasi yang sangat besar (terutama di awal-awal setelah IPO), yang mengindikasikan adanya kluster volatilitas — ciri khas data saham yang cocok dimodelkan dengan ARCH.

Tahap 3: Uji Stasioneritas

Konsep Stasioneritas

Data time series dikatakan stasioner apabila memiliki rata-rata, varians, dan autokovarians yang konstan sepanjang waktu. Model ARIMA dan ARCH mensyaratkan data yang stasioner (setidaknya dalam rata-rata) sebelum dimodelkan. Alat uji yang digunakan adalah Augmented Dickey-Fuller (ADF) Test.

Hipotesis ADF:

  • \(H_0\): Data memiliki unit root (TIDAK stasioner)
  • \(H_1\): Data STASIONER
  • Keputusan: Tolak \(H_0\) jika p-value < 0,05

Uji ADF pada Data Level

adf_level <- adf.test(harga, alternative = "stationary")

hasil_adf_level <- data.frame(
  Keterangan    = c("ADF Statistic", "p-value", "Lag Used", "Kesimpulan"),
  Nilai         = c(
    round(adf_level$statistic, 5),
    round(adf_level$p.value,   5),
    adf_level$parameter,
    ifelse(adf_level$p.value < 0.05, "STASIONER (Tolak H0)",
                                     "TIDAK STASIONER (Gagal Tolak H0)")
  )
)

kable(hasil_adf_level,
      caption = "Hasil Uji ADF pada Data Level",
      booktabs = TRUE, align = c("l","r")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
Hasil Uji ADF pada Data Level
Keterangan Nilai
ADF Statistic -2.25078
p-value 0.472
Lag Used 7
Kesimpulan TIDAK STASIONER (Gagal Tolak H0)

Interpretasi: Dengan p-value 0.472 yang >= 0,05 → data belum stasioner pada level, sehingga perlu dilakukan differencing. Hal ini konsisten dengan visualisasi yang menunjukkan tren penurunan.

Uji ADF pada 1st Difference

Differencing orde pertama dilakukan: \(\Delta y_t = y_t - y_{t-1}\)

harga_diff <- diff(harga)
adf_diff   <- adf.test(harga_diff, alternative = "stationary")

hasil_adf_diff <- data.frame(
  Keterangan = c("ADF Statistic", "p-value", "Lag Used", "Kesimpulan"),
  Nilai      = c(
    round(adf_diff$statistic, 5),
    round(adf_diff$p.value,   5),
    adf_diff$parameter,
    ifelse(adf_diff$p.value < 0.05, "STASIONER (Tolak H0)",
                                    "TIDAK STASIONER (Gagal Tolak H0)")
  )
)

kable(hasil_adf_diff,
      caption = "Hasil Uji ADF pada 1st Difference",
      booktabs = TRUE, align = c("l","r")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
Hasil Uji ADF pada 1st Difference
Keterangan Nilai
ADF Statistic -7.11665
p-value 0.01
Lag Used 7
Kesimpulan STASIONER (Tolak H0)

Interpretasi: Setelah di-differencing satu kali, p-value = 0.01 < 0,05. \(H_0\) ditolak → data stasioner pada 1st Difference. Dengan demikian, orde differencing yang digunakan adalah d = 1, sesuai dengan hasil jurnal.

df_diff <- data.frame(
  Date = data_goto$Date[-1],
  Diff = harga_diff
)

ggplot(df_diff, aes(x = Date, y = Diff)) +
  geom_line(color = "darkgreen", linewidth = 0.5) +
  geom_hline(yintercept = 0, color = "red", linetype = "dashed") +
  labs(title    = "Data 1st Difference Harga Penutupan GOTO",
       subtitle = "Δharga_t = harga_t − harga_(t−1)",
       x = "Tanggal", y = "Δ Harga (Rp)") +
  theme_bw(base_size = 10) +
  theme(plot.title = element_text(face = "bold"))
Data Harga GOTO Setelah 1st Differencing

Data Harga GOTO Setelah 1st Differencing

Interpretasi: Setelah differencing, data berfluktuasi di sekitar nilai 0 tanpa tren yang jelas. Namun terlihat adanya kluster volatilitas — periode dengan fluktuasi besar diikuti periode fluktuasi besar lainnya, dan sebaliknya. Ini mengindikasikan adanya heteroskedastisitas bersyarat yang akan diuji pada tahap selanjutnya.

Tahap 4: Identifikasi Model Mean — Analisis ACF dan PACF

Konsep ACF dan PACF

Untuk menentukan orde model ARIMA(p,d,q), digunakan dua alat:

  • ACF (Autocorrelation Function): mengukur korelasi antara \(y_t\) dan \(y_{t-k}\). Pola ACF yang cuts off (tiba-tiba tidak signifikan) setelah lag \(q\) mengindikasikan komponen MA(q)
  • PACF (Partial Autocorrelation Function): mengukur korelasi antara \(y_t\) dan \(y_{t-k}\) setelah mengeliminasi pengaruh lag di antaranya. Pola PACF yang cuts off setelah lag \(p\) mengindikasikan komponen AR(p)

Karena \(d = 1\) sudah ditetapkan, analisis ACF dan PACF dilakukan pada data 1st difference untuk menentukan \(p\) dan \(q\).

Panduan membaca:

Pola ACF Pola PACF Model yang disarankan
Cuts off setelah lag \(q\) Tails off MA(\(q\))
Tails off Cuts off setelah lag \(p\) AR(\(p\))
Tails off Tails off ARMA(\(p\),\(q\))
Cuts off setelah lag 1 Cuts off setelah lag 1 ARMA(1,1)

Plot ACF dan PACF

par(mfrow = c(2, 1), mar = c(4, 4, 3, 1))

acf_hasil  <- Acf(harga_diff,  lag.max = 12,
                  main = "ACF — 1st Difference Harga GOTO", plot = TRUE)
pacf_hasil <- Pacf(harga_diff, lag.max = 12,
                   main = "PACF — 1st Difference Harga GOTO", plot = TRUE)
ACF dan PACF Data 1st Difference Harga GOTO

ACF dan PACF Data 1st Difference Harga GOTO

par(mfrow = c(1, 1))

Identifikasi Lag yang Signifikan

# Batas signifikansi 95%: ±1.96/sqrt(n)
n         <- length(harga_diff)
batas_sig <- 1.96 / sqrt(n)

# Ambil nilai ACF dan PACF (lag 1 ke atas, lag 0 selalu = 1 diabaikan)
acf_val  <- acf_hasil$acf[-1]   # Hilangkan lag 0
pacf_val <- pacf_hasil$acf

lag_vals <- 1:30

# Lag signifikan ACF
lag_acf_sig <- lag_vals[abs(acf_val[1:30]) > batas_sig]

# Lag signifikan PACF
lag_pacf_sig <- lag_vals[abs(pacf_val[1:30]) > batas_sig]

cat(sprintf("Batas signifikansi 95%% (±1.96/√n): ±%.4f\n\n", batas_sig))
#> Batas signifikansi 95% (±1.96/√n): ±0.0895
cat("=== LAG SIGNIFIKAN PADA ACF ===\n")
#> === LAG SIGNIFIKAN PADA ACF ===
if (length(lag_acf_sig) > 0) {
  cat("Lag:", paste(lag_acf_sig, collapse = ", "), "\n")
  cat("Nilai ACF:\n")
  for (l in lag_acf_sig) {
    cat(sprintf("  Lag %2d: ACF = %+.4f  [|%.4f| > %.4f ✓]\n",
                l, acf_val[l], abs(acf_val[l]), batas_sig))
  }
} else {
  cat("Tidak ada lag yang signifikan.\n")
}
#> Lag: 1, 5, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA 
#> Nilai ACF:
#>   Lag  1: ACF = +0.1787  [|0.1787| > 0.0895 ✓]
#>   Lag  5: ACF = +0.1056  [|0.1056| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: ACF = NA  [|NA| > 0.0895 ✓]
cat("\n=== LAG SIGNIFIKAN PADA PACF ===\n")
#> 
#> === LAG SIGNIFIKAN PADA PACF ===
if (length(lag_pacf_sig) > 0) {
  cat("Lag:", paste(lag_pacf_sig, collapse = ", "), "\n")
  cat("Nilai PACF:\n")
  for (l in lag_pacf_sig) {
    cat(sprintf("  Lag %2d: PACF = %+.4f  [|%.4f| > %.4f ✓]\n",
                l, pacf_val[l], abs(pacf_val[l]), batas_sig))
  }
} else {
  cat("Tidak ada lag yang signifikan.\n")
}
#> Lag: 1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA 
#> Nilai PACF:
#>   Lag  1: PACF = +0.1787  [|0.1787| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]
#>   Lag NA: PACF = NA  [|NA| > 0.0895 ✓]

Interpretasi ACF dan PACF

Berdasarkan hasil identifikasi lag signifikan di atas, dapat ditarik kesimpulan sebagai berikut:

# Tentukan pola berdasarkan lag signifikan
# ACF: jika hanya lag awal saja yang signifikan → cuts off → MA
# PACF: jika hanya lag awal saja yang signifikan → cuts off → AR

cat("**Ringkasan Pola:**\n\n")

Ringkasan Pola:

cat(paste0("- ACF signifikan pada lag: **",
           ifelse(length(lag_acf_sig)>0, paste(lag_acf_sig, collapse=", "), "tidak ada"),
           "**\n"))
  • ACF signifikan pada lag: 1, 5, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA
cat(paste0("- PACF signifikan pada lag: **",
           ifelse(length(lag_pacf_sig)>0, paste(lag_pacf_sig, collapse=", "), "tidak ada"),
           "**\n\n"))
  • PACF signifikan pada lag: 1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA
cat("Berdasarkan pola tersebut:\n\n")

Berdasarkan pola tersebut:

# Tentukan kandidat p (dari PACF) dan q (dari ACF)
p_kandidat_raw <- if (length(lag_pacf_sig) > 0) unique(c(0, lag_pacf_sig[lag_pacf_sig <= 12])) else c(0, 1)
q_kandidat_raw <- if (length(lag_acf_sig)  > 0) unique(c(0, lag_acf_sig[lag_acf_sig   <= 12])) else c(0, 1)

p_kandidat <- sort(unique(p_kandidat_raw))
q_kandidat <- sort(unique(q_kandidat_raw))

cat(paste0("- Kandidat orde AR(p) dari PACF: p ∈ {",
           paste(p_kandidat, collapse=", "), "}\n"))
  • Kandidat orde AR(p) dari PACF: p ∈ {0, 1}
cat(paste0("- Kandidat orde MA(q) dari ACF : q ∈ {",
           paste(q_kandidat, collapse=", "), "}\n\n"))
  • Kandidat orde MA(q) dari ACF : q ∈ {0, 1, 5}
cat("Semua kombinasi p dan q di atas akan diuji satu per satu pada tahap berikutnya.\n")

Semua kombinasi p dan q di atas akan diuji satu per satu pada tahap berikutnya.

Daftar Kandidat Model ARIMA

# Buat semua kombinasi kandidat ARIMA(p,1,q)
kombinasi <- expand.grid(p = p_kandidat, d = 1, q = q_kandidat)
kombinasi <- kombinasi[!(kombinasi$p == 0 & kombinasi$q == 0), ]  # Hapus ARIMA(0,1,0)
kombinasi <- kombinasi[order(kombinasi$p, kombinasi$q), ]
rownames(kombinasi) <- NULL

tabel_kandidat <- data.frame(
  No    = 1:nrow(kombinasi),
  Model = paste0("ARIMA(", kombinasi$p, ",1,", kombinasi$q, ")")
)

kable(tabel_kandidat,
      caption = "Daftar Kandidat Model ARIMA berdasarkan ACF/PACF",
      booktabs = TRUE, align = c("c","c")) |>
  kable_styling(latex_options = c("hold_position", "striped"),
                full_width = FALSE)
Daftar Kandidat Model ARIMA berdasarkan ACF/PACF
No Model
1 ARIMA(0,1,1)
2 ARIMA(0,1,5)
3 ARIMA(1,1,0)
4 ARIMA(1,1,1)
5 ARIMA(1,1,5)

Tahap 5: Seleksi Model ARIMA — Uji Satu per Satu

Setiap kandidat model diuji berdasarkan tiga kriteria:

  1. Signifikansi parameter — semua koefisien harus signifikan (p-value < 0,05)
  2. Kriteria informasi — AIC dan BIC (lebih kecil = lebih baik)
  3. Uji Ljung-Box residual — p-value > 0,05 (residual white noise)
# Fungsi helper: uji satu model ARIMA
uji_model <- function(p, d, q, data_asli) {
  hasil <- tryCatch({
    model <- Arima(data_asli, order = c(p, d, q))
    resid <- residuals(model)
    lb    <- Box.test(resid, lag = 10, type = "Ljung-Box")

    # Ekstrak koefisien dan p-value
    koef    <- coef(model)
    se_koef <- sqrt(diag(model$var.coef))
    z_stat  <- koef / se_koef
    p_val   <- 2 * (1 - pnorm(abs(z_stat)))
    sig_all <- all(p_val < 0.05)

    list(
      model    = model,
      aic      = round(AIC(model), 4),
      bic      = round(BIC(model), 4),
      lb_pval  = round(lb$p.value, 4),
      lb_stat  = round(lb$statistic, 4),
      sig_all  = sig_all,
      p_vals   = round(p_val, 4),
      koef     = round(koef, 4),
      error    = FALSE
    )
  }, error = function(e) {
    list(error = TRUE, msg = e$message)
  })
  return(hasil)
}

# ---- Jalankan semua kandidat ----
semua_hasil <- list()
tabel_ringkasan <- data.frame(
  Model          = character(),
  AIC            = numeric(),
  BIC            = numeric(),
  LjungBox_pval  = numeric(),
  WN             = character(),
  Par_Sig        = character(),
  Lolos          = character(),
  stringsAsFactors = FALSE
)

for (i in 1:nrow(kombinasi)) {
  p <- kombinasi$p[i]; d <- kombinasi$d[i]; q <- kombinasi$q[i]
  nm <- paste0("ARIMA(", p, ",1,", q, ")")
  cat(paste0("\n---\n\n## Model ", i, ": ", nm, "\n\n"))

  hasil <- uji_model(p, d, q, harga)
  semua_hasil[[nm]] <- hasil

  if (hasil$error) {
    cat(paste0("**Model gagal diestimasi:** ", hasil$msg, "\n\n"))
    tabel_ringkasan <- rbind(tabel_ringkasan, data.frame(
      Model = nm, AIC = NA, BIC = NA,
      LjungBox_pval = NA, WN = "ERROR", Par_Sig = "ERROR", Lolos = "✗"
    ))
    next
  }

  # Tabel koefisien
  df_koef <- data.frame(
    Parameter = names(hasil$koef),
    Koefisien = hasil$koef,
    `p-value`  = hasil$p_vals,
    Signifikan = ifelse(hasil$p_vals < 0.05, "Ya ✓", "Tidak ✗")
  )

  print(kable(df_koef,
        caption = paste("Koefisien", nm),
        booktabs = TRUE, row.names = FALSE,
        col.names = c("Parameter","Koefisien","p-value","Signifikan"),
        align = c("l","r","r","c"),
        digits = 4) |>
    kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE))

  # Hasil kriteria
  wn_ok  <- hasil$lb_pval > 0.05
  sig_ok <- hasil$sig_all
  lolos  <- wn_ok & sig_ok

  cat(paste0("\n**Hasil Evaluasi ", nm, ":**\n\n"))
  cat(paste0("- AIC = **", hasil$aic, "** | BIC = **", hasil$bic, "**\n"))
  cat(paste0("- Ljung-Box (lag=10): statistik = ", hasil$lb_stat,
             ", **p-value = ", hasil$lb_pval, "**",
             " → Residual ", ifelse(wn_ok, "**WHITE NOISE** ✓", "**TIDAK white noise** ✗"), "\n"))
  cat(paste0("- Signifikansi parameter: ",
             ifelse(sig_ok, "**Semua signifikan** ✓", "**Ada yang tidak signifikan** ✗"), "\n"))
  cat(paste0("- **Kesimpulan: Model ", ifelse(lolos, "LOLOS ✓", "TIDAK LOLOS ✗"),
             " semua asumsi**\n\n"))

  tabel_ringkasan <- rbind(tabel_ringkasan, data.frame(
    Model         = nm,
    AIC           = hasil$aic,
    BIC           = hasil$bic,
    LjungBox_pval = hasil$lb_pval,
    WN            = ifelse(wn_ok,  "Ya ✓", "Tidak ✗"),
    Par_Sig       = ifelse(sig_ok, "Ya ✓", "Tidak ✗"),
    Lolos         = ifelse(lolos,  "✓",    "✗"),
    stringsAsFactors = FALSE
  ))
}

Model 1: ARIMA(0,1,1)

Koefisien ARIMA(0,1,1)
Parameter Koefisien p-value Signifikan
ma1 0.1839 0 Ya ✓

Hasil Evaluasi ARIMA(0,1,1):

  • AIC = 3323.4945 | BIC = 3331.8421
  • Ljung-Box (lag=10): statistik = 11.8415, p-value = 0.2958 → Residual WHITE NOISE
  • Signifikansi parameter: Semua signifikan
  • Kesimpulan: Model LOLOS ✓ semua asumsi

Model 2: ARIMA(0,1,5)

Koefisien ARIMA(0,1,5)
Parameter Koefisien p-value Signifikan
ma1 0.1862 0.0000 Ya ✓
ma2 0.0292 0.5319 Tidak ✗
ma3 0.0616 0.2068 Tidak ✗
ma4 0.0664 0.1647 Tidak ✗
ma5 0.1123 0.0082 Ya ✓

Hasil Evaluasi ARIMA(0,1,5):

  • AIC = 3322.5021 | BIC = 3347.5448
  • Ljung-Box (lag=10): statistik = 4.2475, p-value = 0.9355 → Residual WHITE NOISE
  • Signifikansi parameter: Ada yang tidak signifikan
  • Kesimpulan: Model TIDAK LOLOS ✗ semua asumsi

Model 3: ARIMA(1,1,0)

Koefisien ARIMA(1,1,0)
Parameter Koefisien p-value Signifikan
ar1 0.1847 0 Ya ✓

Hasil Evaluasi ARIMA(1,1,0):

  • AIC = 3323.286 | BIC = 3331.6336
  • Ljung-Box (lag=10): statistik = 11.8784, p-value = 0.2933 → Residual WHITE NOISE
  • Signifikansi parameter: Semua signifikan
  • Kesimpulan: Model LOLOS ✓ semua asumsi

Model 4: ARIMA(1,1,1)

Koefisien ARIMA(1,1,1)
Parameter Koefisien p-value Signifikan
ar1 0.1483 0.6876 Tidak ✗
ma1 0.0374 0.9208 Tidak ✗

Hasil Evaluasi ARIMA(1,1,1):

  • AIC = 3325.2835 | BIC = 3337.8048
  • Ljung-Box (lag=10): statistik = 11.7894, p-value = 0.2994 → Residual WHITE NOISE
  • Signifikansi parameter: Ada yang tidak signifikan
  • Kesimpulan: Model TIDAK LOLOS ✗ semua asumsi

Model 5: ARIMA(1,1,5)

Koefisien ARIMA(1,1,5)
Parameter Koefisien p-value Signifikan
ar1 -0.1671 0.5858 Tidak ✗
ma1 0.3494 0.2481 Tidak ✗
ma2 0.0578 0.4079 Tidak ✗
ma3 0.0666 0.1862 Tidak ✗
ma4 0.0798 0.1527 Tidak ✗
ma5 0.1205 0.0053 Ya ✓

Hasil Evaluasi ARIMA(1,1,5):

  • AIC = 3324.1976 | BIC = 3353.4141
  • Ljung-Box (lag=10): statistik = 3.6708, p-value = 0.961 → Residual WHITE NOISE
  • Signifikansi parameter: Ada yang tidak signifikan
  • Kesimpulan: Model TIDAK LOLOS ✗ semua asumsi

Ringkasan Perbandingan Semua Kandidat Model

kable(tabel_ringkasan,
      caption   = "Ringkasan Perbandingan Seluruh Kandidat Model ARIMA",
      booktabs  = TRUE, row.names = FALSE,
      col.names = c("Model","AIC","BIC","LjungBox p-val",
                    "White Noise?","Par. Signifikan?","Lolos Asumsi?"),
      align     = c("l","r","r","r","c","c","c")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE) |>
  column_spec(7, bold = TRUE)
Ringkasan Perbandingan Seluruh Kandidat Model ARIMA
Model AIC BIC LjungBox p-val White Noise? Par. Signifikan? Lolos Asumsi?
ARIMA(0,1,1) 3323.494 3331.842 0.2958 Ya ✓ Ya ✓
ARIMA(0,1,5) 3322.502 3347.545 0.9355 Ya ✓ Tidak ✗
ARIMA(1,1,0) 3323.286 3331.634 0.2933 Ya ✓ Ya ✓
ARIMA(1,1,1) 3325.284 3337.805 0.2994 Ya ✓ Tidak ✗
ARIMA(1,1,5) 3324.198 3353.414 0.9610 Ya ✓ Tidak ✗

Pemilihan Model Terbaik

# Filter model yang lolos semua asumsi
lolos_idx <- which(tabel_ringkasan$Lolos == "✓" &
                   !is.na(tabel_ringkasan$AIC))

if (length(lolos_idx) == 0) {
  # Jika tidak ada yang lolos semua, pilih berdasarkan AIC terkecil
  cat("Tidak ada model yang lolos semua asumsi sekaligus.\n")
  cat("Dipilih model dengan AIC terkecil sebagai kompromi terbaik.\n")
  terpilih_idx   <- which.min(tabel_ringkasan$AIC)
  model_terpilih <- tabel_ringkasan$Model[terpilih_idx]
} else {
  # Dari yang lolos, pilih AIC terkecil
  sub_lolos       <- tabel_ringkasan[lolos_idx, ]
  terpilih_row    <- sub_lolos[which.min(sub_lolos$AIC), ]
  model_terpilih  <- terpilih_row$Model
}

cat(paste0("Model terpilih: **", model_terpilih, "**\n"))
#> Model terpilih: **ARIMA(1,1,0)**
cat(paste0("AIC: ", tabel_ringkasan$AIC[tabel_ringkasan$Model == model_terpilih], "\n"))
#> AIC: 3323.286
# Ekstrak orde p dan q dari nama model
pars_terpilih <- as.integer(strsplit(gsub("ARIMA\\(|\\)", "", model_terpilih), ",")[[1]])
p_final <- pars_terpilih[1]
d_final <- pars_terpilih[2]
q_final <- pars_terpilih[3]

Interpretasi Pemilihan Model:

Model ARIMA(1,1,0) dipilih sebagai model terbaik karena memiliki AIC terkecil di antara model-model yang memenuhi asumsi white noise residual dan signifikansi parameter. Konsisten dengan hasil jurnal yang menggunakan ARIMA(1,1,0) berdasarkan nilai AIC terkecil dengan tetap mempertahankan heteroskedastisitas yang perlu dimodelkan lebih lanjut.

Tahap 6: Estimasi Model Mean dan Uji Residual

Estimasi Model ARIMA Terpilih

# Fit model terpilih
model_arima <- Arima(harga, order = c(p_final, d_final, q_final))
cat(paste0("Model yang diestimasi: ARIMA(",
           p_final,",",d_final,",",q_final,")\n\n"))
#> Model yang diestimasi: ARIMA(1,1,0)
print(summary(model_arima))
#> Series: harga 
#> ARIMA(1,1,0) 
#> 
#> Coefficients:
#>          ar1
#>       0.1847
#> s.e.  0.0449
#> 
#> sigma^2 = 59.11:  log likelihood = -1659.64
#> AIC=3323.29   AICc=3323.31   BIC=3331.63
#> 
#> Training set error measures:
#>                      ME     RMSE      MAE        MPE     MAPE      MASE
#> Training set -0.5314175 7.671996 4.813096 -0.3762998 3.170115 0.9923909
#>                      ACF1
#> Training set -0.005232772
residual_arima <- residuals(model_arima)

Interpretasi Koefisien:

koef  <- coef(model_arima)
se_k  <- sqrt(diag(model_arima$var.coef))
z_k   <- koef / se_k
pv_k  <- 2 * (1 - pnorm(abs(z_k)))

df_koef_final <- data.frame(
  Parameter = names(koef),
  Estimate  = round(koef, 5),
  Std.Error = round(se_k, 5),
  z.stat    = round(z_k,  4),
  p.value   = round(pv_k, 5),
  Signifikan = ifelse(pv_k < 0.05, "Ya (< 0.05) ✓", "Tidak (≥ 0.05) ✗")
)

kable(df_koef_final,
      caption  = paste("Koefisien Model", model_terpilih),
      booktabs = TRUE, row.names = FALSE,
      align    = c("l","r","r","r","r","c")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
Koefisien Model ARIMA(1,1,0)
Parameter Estimate Std.Error z.stat p.value Signifikan
ar1 0.18466 0.04491 4.1117 0.00004 Ya (< 0.05) ✓

Uji White Noise Residual (Ljung-Box Test)

Sebelum melanjutkan ke uji efek ARCH, residual ARIMA harus dipastikan bersifat white noise — tidak mengandung pola autokorelasi yang belum tertangkap oleh model.

Hipotesis Ljung-Box:

  • \(H_0\): Residual tidak berkorelasi (white noise)
  • \(H_1\): Ada autokorelasi pada residual
  • Keputusan: Gagal tolak \(H_0\) jika p-value > 0,05
lb_hasil <- data.frame(
  Lag       = c(5, 10, 15, 20),
  Statistik = NA,
  p.value   = NA,
  Kesimpulan = NA
)
for (i in 1:nrow(lb_hasil)) {
  lb_i <- Box.test(residual_arima, lag = lb_hasil$Lag[i], type = "Ljung-Box")
  lb_hasil$Statistik[i]  <- round(lb_i$statistic, 4)
  lb_hasil$p.value[i]    <- round(lb_i$p.value, 4)
  lb_hasil$Kesimpulan[i] <- ifelse(lb_i$p.value > 0.05, "White Noise ✓", "Tidak WN ✗")
}

kable(lb_hasil,
      caption  = "Hasil Uji Ljung-Box pada Residual ARIMA",
      booktabs = TRUE, row.names = FALSE,
      align    = c("c","r","r","c")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
Hasil Uji Ljung-Box pada Residual ARIMA
Lag Statistik p.value Kesimpulan
5 7.8219 0.1663 White Noise ✓
10 11.8784 0.2933 White Noise ✓
15 23.1018 0.0820 White Noise ✓
20 34.9122 0.0206 Tidak WN ✗

Interpretasi: Berdasarkan hasil Ljung-Box di atas, residual model ARIMA(1,1,0) pada beberapa lag menunjukkan p-value < 0,05. Namun model tetap digunakan karena memiliki AIC terkecil, dan komponen volatilitas akan ditangkap oleh model ARCH. Langkah selanjutnya adalah menguji apakah residual mengandung efek heteroskedastisitas bersyarat (efek ARCH).

par(mfrow = c(2,2), mar = c(4,4,3,1))

# Plot 1: Residual time series
plot(residual_arima, type = "l", col = "steelblue",
     main = "Residual ARIMA", xlab = "Observasi", ylab = "Residual")
abline(h = 0, col = "red", lty = 2)

# Plot 2: ACF Residual
acf(residual_arima, lag.max = 20, main = "ACF Residual ARIMA",
    col = "steelblue")

# Plot 3: Residual kuadrat (preview ARCH)
plot(residual_arima^2, type = "l", col = "firebrick",
     main = "Residual Kuadrat (Kluster Volatilitas?)",
     xlab = "Observasi", ylab = "Residual²")

# Plot 4: Histogram residual
hist(residual_arima, breaks = 40, col = "lightblue", border = "white",
     main = "Distribusi Residual", xlab = "Residual", probability = TRUE)
curve(dnorm(x, mean = mean(residual_arima), sd = sd(residual_arima)),
      add = TRUE, col = "red", lwd = 2)
Diagnostic Plot Residual ARIMA

Diagnostic Plot Residual ARIMA

par(mfrow = c(1,1))

Interpretasi plot residual:

  • Residual time series: Berfluktuasi di sekitar nol tanpa tren, namun terlihat jelas periode dengan variabilitas tinggi dan rendah yang berkelompok (volatility clustering)
  • ACF residual: Tidak ada spike yang melewati garis biru signifikansi → tidak ada autokorelasi yang tersisa
  • Residual kuadrat: Terdapat pola pengelompokan (clustering) yang jelas — ini adalah bukti visual adanya efek ARCH (heteroskedastisitas bersyarat)
  • Histogram: Distribusi residual menceng dan memiliki ekor lebih tebal dari kurva normal merah → distribusi non-normal (leptokurtik), konsisten dengan data saham

Tahap 7: Uji Efek ARCH (ARCH-LM Test)

Konsep Efek ARCH

Efek ARCH (Autoregressive Conditional Heteroscedasticity) terjadi ketika varians dari residual tidak konstan melainkan bergantung pada besarnya guncangan (shock) di periode sebelumnya. Secara matematis:

\[\sigma^2_t = \omega + \alpha_1 \varepsilon^2_{t-1} + \alpha_2 \varepsilon^2_{t-2} + \ldots + \alpha_p \varepsilon^2_{t-p}\]

Efek ini sangat umum pada data harga saham karena adanya volatility clustering — periode bergejolak cenderung diikuti periode bergejolak, dan periode tenang diikuti periode tenang.

Hipotesis ARCH-LM Test:

  • \(H_0\): Tidak ada efek ARCH (varians konstan / homoskedastis)
  • \(H_1\): Ada efek ARCH (heteroskedastisitas bersyarat)
  • Keputusan: Tolak \(H_0\) jika p-value < 0,05

Hasil ARCH-LM Test

arch_df <- data.frame(
  Lag        = c(1, 5, 10, 15),
  Chi.sq     = NA,
  df         = NA,
  p.value    = NA,
  Kesimpulan = NA
)
for (i in 1:nrow(arch_df)) {
  at <- ArchTest(residual_arima, lags = arch_df$Lag[i])
  arch_df$Chi.sq[i]     <- round(at$statistic, 4)
  arch_df$df[i]         <- at$parameter
  arch_df$p.value[i]    <- round(at$p.value, 6)
  arch_df$Kesimpulan[i] <- ifelse(at$p.value < 0.05,
                                  "Ada Efek ARCH ✓ → Lanjut",
                                  "Tidak Ada Efek ARCH ✗")
}

kable(arch_df,
      caption  = "Hasil ARCH-LM Test pada Berbagai Lag",
      booktabs = TRUE, row.names = FALSE,
      col.names = c("Lag","Chi-square","df","p-value","Kesimpulan"),
      align    = c("c","r","c","r","l")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
Hasil ARCH-LM Test pada Berbagai Lag
Lag Chi-square df p-value Kesimpulan
1 28.1933 1 0 Ada Efek ARCH ✓ → Lanjut
5 47.1804 5 0 Ada Efek ARCH ✓ → Lanjut
10 83.3129 10 0 Ada Efek ARCH ✓ → Lanjut
15 90.6558 15 0 Ada Efek ARCH ✓ → Lanjut
arch_lag1 <- ArchTest(residual_arima, lags = 1)
cat("Hasil Detail ARCH-LM Test (lag = 1, sesuai jurnal):\n")
#> Hasil Detail ARCH-LM Test (lag = 1, sesuai jurnal):
print(arch_lag1)
#> 
#>  ARCH LM-test; Null hypothesis: no ARCH effects
#> 
#> data:  residual_arima
#> Chi-squared = 28.193, df = 1, p-value = 0.0000001098

Interpretasi: P-value ARCH-LM = 0 yang sangat jauh di bawah 0,05. \(H_0\) ditolak → terdapat efek ARCH yang signifikan pada residual model ARIMA. Hal ini mengkonfirmasi bahwa varians residual tidak konstan dan bergantung pada shock periode sebelumnya. Oleh karena itu, pemodelan ARCH diperlukan untuk menangkap pola heteroskedastisitas bersyarat ini, sesuai dengan kesimpulan pada jurnal.

Tahap 8: Penentuan Orde ARCH(p)

Analisis ACF Residual Kuadrat

Orde \(p\) pada model ARCH(\(p\)) ditentukan berdasarkan plot ACF dari residual kuadrat (\(\hat{\varepsilon}^2_t\)). Lag terakhir yang secara signifikan berbeda dari nol pada ACF residual kuadrat mengindikasikan orde \(p\) yang sesuai.

par(mfrow = c(2, 1), mar = c(4, 4, 3, 1))

acf_rk  <- Acf(residual_arima^2,  lag.max = 20,
                main = "ACF Residual Kuadrat — Panduan Orde ARCH(p)")
pacf_rk <- Pacf(residual_arima^2, lag.max = 20,
                 main = "PACF Residual Kuadrat")
ACF dan PACF Residual Kuadrat

ACF dan PACF Residual Kuadrat

par(mfrow = c(1, 1))

Lag Signifikan pada ACF Residual Kuadrat

acf_rk_val  <- acf_rk$acf[-1]
pacf_rk_val <- pacf_rk$acf

n_rk  <- length(residual_arima^2)
bts   <- 1.96 / sqrt(n_rk)

lag_acf_rk  <- which(abs(acf_rk_val[1:20])  > bts)
lag_pacf_rk <- which(abs(pacf_rk_val[1:20]) > bts)

cat(sprintf("Batas signifikansi 95%%: ±%.4f\n\n", bts))
#> Batas signifikansi 95%: ±0.0894
cat("=== LAG SIGNIFIKAN ACF RESIDUAL KUADRAT ===\n")
#> === LAG SIGNIFIKAN ACF RESIDUAL KUADRAT ===
if (length(lag_acf_rk) > 0) {
  for (l in lag_acf_rk) {
    cat(sprintf("  Lag %2d: ACF = %+.4f  [|%.4f| > %.4f ✓]\n",
                l, acf_rk_val[l], abs(acf_rk_val[l]), bts))
  }
} else {
  cat("  Tidak ada lag yang signifikan.\n")
}
#>   Lag  1: ACF = +0.2423  [|0.2423| > 0.0894 ✓]
#>   Lag  2: ACF = +0.1149  [|0.1149| > 0.0894 ✓]
#>   Lag  3: ACF = +0.2159  [|0.2159| > 0.0894 ✓]
#>   Lag  8: ACF = +0.1534  [|0.1534| > 0.0894 ✓]
#>   Lag  9: ACF = +0.3026  [|0.3026| > 0.0894 ✓]
#>   Lag 10: ACF = +0.1180  [|0.1180| > 0.0894 ✓]
#>   Lag 11: ACF = +0.2008  [|0.2008| > 0.0894 ✓]
#>   Lag 12: ACF = +0.2038  [|0.2038| > 0.0894 ✓]
#>   Lag 19: ACF = +0.1055  [|0.1055| > 0.0894 ✓]
#>   Lag 20: ACF = +0.1048  [|0.1048| > 0.0894 ✓]
cat("\n=== LAG SIGNIFIKAN PACF RESIDUAL KUADRAT ===\n")
#> 
#> === LAG SIGNIFIKAN PACF RESIDUAL KUADRAT ===
if (length(lag_pacf_rk) > 0) {
  for (l in lag_pacf_rk) {
    cat(sprintf("  Lag %2d: PACF = %+.4f  [|%.4f| > %.4f ✓]\n",
                l, pacf_rk_val[l], abs(pacf_rk_val[l]), bts))
  }
} else {
  cat("  Tidak ada lag yang signifikan.\n")
}
#>   Lag  1: PACF = +0.2423  [|0.2423| > 0.0894 ✓]
#>   Lag  3: PACF = +0.1869  [|0.1869| > 0.0894 ✓]
#>   Lag  8: PACF = +0.1304  [|0.1304| > 0.0894 ✓]
#>   Lag  9: PACF = +0.2508  [|0.2508| > 0.0894 ✓]
#>   Lag 11: PACF = +0.1331  [|0.1331| > 0.0894 ✓]
# Tentukan orde maksimal yang dicoba
p_max_arch <- max(3, max(lag_acf_rk, lag_pacf_rk, na.rm = TRUE))
p_max_arch <- min(p_max_arch, 5)  # Batasi hingga 5 agar tidak terlalu kompleks
cat(sprintf("\nOrde maksimal ARCH yang akan diuji: ARCH(1) s.d. ARCH(%d)\n", p_max_arch))
#> 
#> Orde maksimal ARCH yang akan diuji: ARCH(1) s.d. ARCH(5)

Interpretasi: Lag-lag yang signifikan pada ACF residual kuadrat mengindikasikan bahwa shock pada lag tersebut masih berpengaruh terhadap varians saat ini. Semakin banyak lag signifikan, semakin tinggi orde \(p\) yang mungkin dibutuhkan. Lag yang paling awal signifikan menjadi kandidat utama orde ARCH.

Perbandingan AIC/BIC Berbagai Orde ARCH

# Uji ARCH(1) sampai ARCH(p_max_arch)
arch_comp <- data.frame(
  Model  = character(),
  AIC    = numeric(),
  BIC    = numeric(),
  Konvergen = character(),
  stringsAsFactors = FALSE
)

for (p_arch in 1:p_max_arch) {
  spec_coba <- ugarchspec(
    variance.model     = list(model = "sGARCH", garchOrder = c(p_arch, 0)),
    mean.model         = list(armaOrder = c(p_final, 0), include.mean = TRUE),
    distribution.model = "norm"
  )
  tryCatch({
    fit_coba   <- ugarchfit(spec = spec_coba, data = harga_diff,
                            solver = "hybrid", solver.control = list(trace = 0))
    ic         <- infocriteria(fit_coba)
    arch_comp  <- rbind(arch_comp, data.frame(
      Model     = paste0("ARCH(", p_arch, ")"),
      AIC       = round(ic[1], 6),
      BIC       = round(ic[2], 6),
      Konvergen = "Ya ✓"
    ))
  }, error = function(e) {
    arch_comp <<- rbind(arch_comp, data.frame(
      Model = paste0("ARCH(", p_arch, ")"), AIC = NA, BIC = NA, Konvergen = "Tidak ✗"
    ))
  })
}

# Tandai model terbaik
best_arch_idx <- which.min(arch_comp$AIC)

kable(arch_comp,
      caption  = "Perbandingan AIC/BIC Model ARCH dengan Berbagai Orde",
      booktabs = TRUE, row.names = FALSE,
      align    = c("l","r","r","c")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE) |>
  row_spec(best_arch_idx, bold = TRUE, background = "#D5F5E3")
Perbandingan AIC/BIC Model ARCH dengan Berbagai Orde
Model AIC BIC Konvergen
ARCH(1) 6.700165 6.734946 Ya ✓
ARCH(2) 6.652655 6.696132 Ya ✓
ARCH(3) 6.490767 6.542939 Ya ✓
ARCH(4) 6.484983 6.545851 Ya ✓
ARCH(5) 6.453061 6.522624 Ya ✓
p_arch_terpilih <- as.integer(gsub("ARCH\\(|\\)", "", arch_comp$Model[best_arch_idx]))
cat(paste0("Model ARCH terpilih: ARCH(", p_arch_terpilih, ")\n"))
#> Model ARCH terpilih: ARCH(5)
cat(paste0("AIC terkecil      : ", arch_comp$AIC[best_arch_idx], "\n"))
#> AIC terkecil      : 6.453061
cat(paste0("BIC               : ", arch_comp$BIC[best_arch_idx], "\n"))
#> BIC               : 6.522624
cat(paste0("\nKatatan: Sesuai jurnal (ARCH(1) = GARCH(1,0) dengan q=0),\n"))
#> 
#> Katatan: Sesuai jurnal (ARCH(1) = GARCH(1,0) dengan q=0),
cat(paste0("model yang dipilih untuk estimasi adalah ARCH(", p_arch_terpilih, ").\n"))
#> model yang dipilih untuk estimasi adalah ARCH(5).

Interpretasi: Berdasarkan kombinasi informasi dari ACF residual kuadrat dan perbandingan AIC/BIC, ARCH(5) dipilih sebagai model terbaik (disorot hijau pada tabel). Model dengan AIC terkecil secara statistik dianggap paling efisien dalam menjelaskan pola heteroskedastisitas bersyarat pada data GOTO.

Tahap 9: Estimasi Model ARCH

Spesifikasi dan Fitting Model

Model ARCH(\(p\)) secara matematis diekspresikan sebagai:

Persamaan Mean: \[\Delta y_t = \mu + \phi_1 \Delta y_{t-1} + \varepsilon_t\]

Persamaan Varians: \[\sigma^2_t = \omega + \alpha_1 \varepsilon^2_{t-1} + \alpha_2 \varepsilon^2_{t-2} + \ldots + \alpha_p \varepsilon^2_{t-p}\]

di mana \(\varepsilon_t = \sigma_t z_t\) dengan \(z_t \sim N(0,1)\), dan seluruh \(\alpha_i \geq 0\) serta \(\sum \alpha_i < 1\) untuk menjamin stasioneritas dalam varians.

spec_arch_final <- ugarchspec(
  variance.model     = list(model = "sGARCH",
                            garchOrder = c(p_arch_terpilih, 0)),
  mean.model         = list(armaOrder = c(p_final, 0),
                            include.mean = TRUE),
  distribution.model = "norm"
)

cat(paste0("Proses fitting ARCH(", p_arch_terpilih,
           ") dengan mean AR(", p_final, ")...\n"))
#> Proses fitting ARCH(5) dengan mean AR(1)...
model_arch <- ugarchfit(spec   = spec_arch_final,
                        data   = harga_diff,
                        solver = "hybrid",
                        solver.control = list(trace = 0))

cat("\nHasil lengkap estimasi:\n")
#> 
#> Hasil lengkap estimasi:
show(model_arch)
#> 
#> *---------------------------------*
#> *          GARCH Model Fit        *
#> *---------------------------------*
#> 
#> Conditional Variance Dynamics    
#> -----------------------------------
#> GARCH Model  : sGARCH(5,0)
#> Mean Model   : ARFIMA(1,0,0)
#> Distribution : norm 
#> 
#> Optimal Parameters
#> ------------------------------------
#>         Estimate  Std. Error   t value Pr(>|t|)
#> mu     -0.031934    0.183906 -0.173642 0.862147
#> ar1    -0.003625    0.058109 -0.062385 0.950256
#> omega   8.120100    1.658700  4.895461 0.000001
#> alpha1  0.308888    0.072889  4.237801 0.000023
#> alpha2  0.067144    0.048017  1.398358 0.162005
#> alpha3  0.506566    0.084405  6.001596 0.000000
#> alpha4  0.017744    0.025974  0.683145 0.494515
#> alpha5  0.098658    0.042951  2.296974 0.021620
#> 
#> Robust Standard Errors:
#>         Estimate  Std. Error   t value Pr(>|t|)
#> mu     -0.031934    0.221635 -0.144083 0.885435
#> ar1    -0.003625    0.082223 -0.044089 0.964833
#> omega   8.120100    2.606770  3.115005 0.001839
#> alpha1  0.308888    0.103955  2.971345 0.002965
#> alpha2  0.067144    0.063322  1.060368 0.288977
#> alpha3  0.506566    0.195539  2.590611 0.009581
#> alpha4  0.017744    0.025708  0.690198 0.490070
#> alpha5  0.098658    0.067453  1.462625 0.143570
#> 
#> LogLikelihood : -1540.735 
#> 
#> Information Criteria
#> ------------------------------------
#>                    
#> Akaike       6.4531
#> Bayes        6.5226
#> Shibata      6.4525
#> Hannan-Quinn 6.4804
#> 
#> Weighted Ljung-Box Test on Standardized Residuals
#> ------------------------------------
#>                         statistic p-value
#> Lag[1]                      1.972  0.1603
#> Lag[2*(p+q)+(p+q)-1][2]     1.972  0.2230
#> Lag[4*(p+q)+(p+q)-1][5]     2.337  0.6133
#> d.o.f=1
#> H0 : No serial correlation
#> 
#> Weighted Ljung-Box Test on Standardized Squared Residuals
#> ------------------------------------
#>                          statistic p-value
#> Lag[1]                      0.1406  0.7077
#> Lag[2*(p+q)+(p+q)-1][14]    3.6079  0.9146
#> Lag[4*(p+q)+(p+q)-1][24]    6.9562  0.9336
#> d.o.f=5
#> 
#> Weighted ARCH LM Tests
#> ------------------------------------
#>              Statistic Shape Scale P-Value
#> ARCH Lag[6]     0.1207 0.500 2.000  0.7283
#> ARCH Lag[8]     0.6826 1.480 1.774  0.8517
#> ARCH Lag[10]    2.2816 2.424 1.650  0.7175
#> 
#> Nyblom stability test
#> ------------------------------------
#> Joint Statistic:  4.4785
#> Individual Statistics:              
#> mu     0.06391
#> ar1    0.30763
#> omega  1.01833
#> alpha1 1.58250
#> alpha2 0.56003
#> alpha3 0.53731
#> alpha4 0.42105
#> alpha5 0.66193
#> 
#> Asymptotic Critical Values (10% 5% 1%)
#> Joint Statistic:          1.89 2.11 2.59
#> Individual Statistic:     0.35 0.47 0.75
#> 
#> Sign Bias Test
#> ------------------------------------
#>                    t-value   prob sig
#> Sign Bias           0.5112 0.6094    
#> Negative Sign Bias  0.8395 0.4016    
#> Positive Sign Bias  0.8113 0.4176    
#> Joint Effect        1.4506 0.6937    
#> 
#> 
#> Adjusted Pearson Goodness-of-Fit Test:
#> ------------------------------------
#>   group statistic
#> 1    20     135.8
#> 2    30     223.1
#> 3    40     310.5
#> 4    50     406.9
#>                                                      p-value(g-1)
#> 1 0.0000000000000000001138712342520378806428812956674789802491432
#> 2 0.0000000000000000000000000000000762904500237385388818414644341
#> 3 0.0000000000000000000000000000000000000000000526850370588221432
#> 4 0.0000000000000000000000000000000000000000000000000000000007063
#> 
#> 
#> Elapsed time : 0.4934721

Tabel Parameter Estimasi

mat_koef <- model_arch@fit$matcoef
df_par   <- data.frame(
  Parameter  = rownames(mat_koef),
  Estimate   = round(mat_koef[,1], 6),
  Std.Error  = round(mat_koef[,2], 6),
  t.Stat     = round(mat_koef[,3], 4),
  p.value    = round(mat_koef[,4], 6),
  Signifikan = ifelse(mat_koef[,4] < 0.05, "Ya ✓", "Tidak ✗")
)

kable(df_par,
      caption  = paste0("Parameter Estimasi Model ARCH(",
                        p_arch_terpilih, ") dengan Mean AR(", p_final, ")"),
      booktabs = TRUE, row.names = FALSE,
      align    = c("l","r","r","r","r","c")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE) |>
  pack_rows("Persamaan Mean", 1, p_final + 1) |>
  pack_rows("Persamaan Varians", p_final + 2, nrow(df_par))
Parameter Estimasi Model ARCH(5) dengan Mean AR(1)
Parameter Estimate Std.Error t.Stat p.value Signifikan
Persamaan Mean
mu -0.031934 0.183906 -0.1736 0.862147 Tidak ✗
ar1 -0.003625 0.058109 -0.0624 0.950256 Tidak ✗
Persamaan Varians
omega 8.120100 1.658700 4.8955 0.000001 Ya ✓
alpha1 0.308888 0.072889 4.2378 0.000023 Ya ✓
alpha2 0.067144 0.048017 1.3984 0.162005 Tidak ✗
alpha3 0.506566 0.084405 6.0016 0.000000 Ya ✓
alpha4 0.017744 0.025974 0.6831 0.494515 Tidak ✗
alpha5 0.098658 0.042951 2.2970 0.021620 Ya ✓

Tahap 10: Evaluasi Model ARCH

Signifikansi Parameter

mat_k <- model_arch@fit$matcoef
semua_sig <- all(mat_k[,4] < 0.05)
cat(paste0("Seluruh parameter signifikan: **",
           ifelse(semua_sig, "YA ✓", "TIDAK ✗"), "**\n\n"))

Seluruh parameter signifikan: TIDAK ✗

if (!semua_sig) {
  tidak_sig <- rownames(mat_k)[mat_k[,4] >= 0.05]
  cat(paste0("Parameter tidak signifikan: ", paste(tidak_sig, collapse=", "), "\n\n"))
  cat("Catatan: Model tetap digunakan karena AIC terkecil dan secara keseluruhan\n")
  cat("menjelaskan pola volatilitas dengan baik.\n")
} else {
  cat("Semua parameter memiliki p-value < 0,05, sehingga setiap parameter\n")
  cat("memberikan kontribusi yang signifikan secara statistik.\n")
}

Parameter tidak signifikan: mu, ar1, alpha2, alpha4

Catatan: Model tetap digunakan karena AIC terkecil dan secara keseluruhan menjelaskan pola volatilitas dengan baik.

Kriteria Informasi

ic_arch <- infocriteria(model_arch)
df_ic <- data.frame(
  Kriteria = c("AIC (Akaike)", "BIC (Schwarz)", "Hannan-Quinn", "Shibata"),
  Nilai    = round(as.numeric(ic_arch), 6)
)

kable(df_ic,
      caption  = paste0("Kriteria Informasi Model ARCH(", p_arch_terpilih, ")"),
      booktabs = TRUE, row.names = FALSE,
      align    = c("l","r")) |>
  kable_styling(latex_options = c("hold_position","striped"),
                full_width = FALSE)
Kriteria Informasi Model ARCH(5)
Kriteria Nilai
AIC (Akaike) 6.453061
BIC (Schwarz) 6.522624
Hannan-Quinn 6.452518
Shibata 6.480405

Uji Residual Standar (White Noise)

resid_std <- residuals(model_arch, standardize = TRUE)

lb_std_df <- data.frame(
  Lag = c(5, 10, 15, 20), Chi.sq = NA, p.value = NA, Kesimpulan = NA
)
for (i in 1:nrow(lb_std_df)) {
  lb_i <- Box.test(resid_std, lag = lb_std_df$Lag[i], type = "Ljung-Box")
  lb_std_df$Chi.sq[i]     <- round(lb_i$statistic, 4)
  lb_std_df$p.value[i]    <- round(lb_i$p.value, 4)
  lb_std_df$Kesimpulan[i] <- ifelse(lb_i$p.value > 0.05,
                                    "White Noise ✓", "Tidak WN ✗")
}

kable(lb_std_df,
      caption  = "Ljung-Box Test pada Residual Standar Model ARCH",
      booktabs = TRUE, row.names = FALSE,
      col.names = c("Lag","Chi-square","p-value","Kesimpulan"),
      align    = c("c","r","r","c")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
Ljung-Box Test pada Residual Standar Model ARCH
Lag Chi-square p-value Kesimpulan
5 3.4767 0.6269 White Noise ✓
10 5.5983 0.8478 White Noise ✓
15 12.1398 0.6684 White Noise ✓
20 16.1478 0.7074 White Noise ✓

Uji Sisa Efek ARCH pada Residual Standar

arch_resid_df <- data.frame(
  Lag = c(1, 5, 10), Chi.sq = NA, p.value = NA, Kesimpulan = NA
)
for (i in 1:nrow(arch_resid_df)) {
  at_i <- ArchTest(resid_std, lags = arch_resid_df$Lag[i])
  arch_resid_df$Chi.sq[i]     <- round(at_i$statistic, 4)
  arch_resid_df$p.value[i]    <- round(at_i$p.value, 4)
  arch_resid_df$Kesimpulan[i] <- ifelse(at_i$p.value > 0.05,
                                        "Tidak Ada Sisa ARCH ✓",
                                        "Masih Ada Efek ARCH ✗")
}

kable(arch_resid_df,
      caption  = "ARCH-LM Test pada Residual Standar Model ARCH (Cek Sisa Efek)",
      booktabs = TRUE, row.names = FALSE,
      col.names = c("Lag","Chi-square","p-value","Kesimpulan"),
      align    = c("c","r","r","c")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
ARCH-LM Test pada Residual Standar Model ARCH (Cek Sisa Efek)
Lag Chi-square p-value Kesimpulan
1 0.1395 0.7088 Tidak Ada Sisa ARCH ✓
5 2.1949 0.8216 Tidak Ada Sisa ARCH ✓
10 4.9600 0.8938 Tidak Ada Sisa ARCH ✓
arch_sisa_ok <- all(arch_resid_df$p.value > 0.05)
cat(paste0("\nKesimpulan: ",
           ifelse(arch_sisa_ok,
                  "Model ARCH berhasil menangkap seluruh efek heteroskedastisitas. ✓",
                  "Masih ada sisa efek ARCH. Pertimbangkan menaikkan orde p. ✗"), "\n"))
#> 
#> Kesimpulan: Model ARCH berhasil menangkap seluruh efek heteroskedastisitas. ✓

Diagnostic Plots

par(mfrow = c(2, 2), mar = c(4, 4, 3, 1))

# 1. Residual standar time series
plot(resid_std, type = "l", col = "steelblue",
     main = "Residual Standar ARCH",
     xlab = "Observasi", ylab = "Residual Standar")
abline(h = c(0, 2, -2), col = c("red","orange","orange"), lty = c(2,2,2))

# 2. ACF residual standar
acf(resid_std, lag.max = 20,
    main = "ACF Residual Standar",
    col = "steelblue")

# 3. ACF residual standar kuadrat
acf(resid_std^2, lag.max = 20,
    main = "ACF Residual Standar Kuadrat",
    col = "firebrick")

# 4. QQ-Plot
qqnorm(resid_std, main = "QQ-Plot Residual Standar",
       col = "steelblue", pch = 20, cex = 0.5)
qqline(resid_std, col = "red", lwd = 2)
Diagnostic Plot Model ARCH — Residual Standar

Diagnostic Plot Model ARCH — Residual Standar

par(mfrow = c(1, 1))

Interpretasi Diagnostic Plot:

  • Residual standar: Berfluktuasi di sekitar nol, sebagian besar berada di antara garis \(\pm 2\) (batas 2 standar deviasi). Beberapa titik ekstrem di luar batas ini merepresentasikan periode gejolak harga yang sangat besar
  • ACF residual standar: Tidak ada spike yang melewati batas signifikansi → tidak ada sisa autokorelasi pada persamaan mean → model mean sudah memadai
  • ACF residual standar kuadrat: Tidak ada spike signifikan → efek ARCH telah berhasil ditangkap sepenuhnya oleh model.
  • QQ-Plot: Titik-titik menyimpang dari garis diagonal di bagian ekor (terutama ekor kiri dan kanan) → distribusi residual memiliki ekor lebih tebal dari normal (heavy-tailed). Hal ini umum pada data saham dan dapat diatasi dengan menggunakan distribusi Student-t (distribution.model = "std") pada estimasi
sigma_arch <- sigma(model_arch)
df_sigma   <- data.frame(
  Date  = data_goto$Date[-1],
  Sigma = sigma_arch
)

ggplot(df_sigma, aes(x = Date, y = Sigma)) +
  geom_line(color = "firebrick", linewidth = 0.6) +
  labs(
    title    = paste0("Volatilitas Kondisional ARCH(", p_arch_terpilih,
                      ") — σ_t"),
    subtitle = "Tinggi = periode ketidakpastian harga tertinggi",
    x = "Tanggal", y = "Conditional Std. Dev. (σ_t)"
  ) +
  theme_bw(base_size = 10) +
  theme(plot.title = element_text(face = "bold"))
Volatilitas Kondisional Hasil Model ARCH

Volatilitas Kondisional Hasil Model ARCH

Interpretasi Volatilitas Kondisional: Plot \(\sigma_t\) memperlihatkan bahwa volatilitas saham GOTO tidak konstan sepanjang waktu. Terdapat beberapa periode dengan puncak volatilitas yang sangat tinggi — kemungkinan besar berkaitan dengan peristiwa-peristiwa besar seperti pengumuman merger dengan TikTok, rilis laporan keuangan, atau guncangan pasar modal secara umum. Pola ini membuktikan perlunya model ARCH dalam memodelkan volatilitas GOTO.

Tahap 11: Interpretasi Model Akhir

Persamaan Model

mu_v    <- round(coef(model_arch)["mu"],     4)
ar1_v   <- round(coef(model_arch)["ar1"],    4)
omega_v <- round(coef(model_arch)["omega"],  4)

# Ambil semua alpha
alpha_names <- grep("^alpha", names(coef(model_arch)), value = TRUE)
alpha_vals  <- round(coef(model_arch)[alpha_names], 4)

cat("### Persamaan Mean\n\n")

Persamaan Mean

if (p_final == 1) {
  cat(paste0("$$\\Delta\\hat{y}_t = ",
             sprintf("%.4f", mu_v), " + (",
             sprintf("%.4f", ar1_v), ") \\Delta y_{t-1} + \\varepsilon_t$$\n\n"))
}

\[\Delta\hat{y}_t = -0.0319 + (-0.0036) \Delta y_{t-1} + \varepsilon_t\]

cat("### Persamaan Varians\n\n")

Persamaan Varians

var_eq <- paste0("$$\\hat{\\sigma}^2_t = ", sprintf("%.4f", omega_v))
for (j in seq_along(alpha_vals)) {
  var_eq <- paste0(var_eq, " + ", sprintf("%.4f", alpha_vals[j]),
                   " \\varepsilon^2_{t-", j, "}")
}
var_eq <- paste0(var_eq, "$$")
cat(var_eq, "\n")

\[\hat{\sigma}^2_t = 8.1201 + 0.3089 \varepsilon^2_{t-1} + 0.0671 \varepsilon^2_{t-2} + 0.5066 \varepsilon^2_{t-3} + 0.0177 \varepsilon^2_{t-4} + 0.0987 \varepsilon^2_{t-5}\]

Interpretasi Parameter

persistensi <- sum(alpha_vals)
vu_long     <- if (persistensi < 1) omega_v / (1 - persistensi) else NA

cat("**Interpretasi Persamaan Mean:**\n\n")

Interpretasi Persamaan Mean:

cat(paste0("- **$\\mu$ = ", mu_v, "**: Rata-rata perubahan harga harian GOTO adalah Rp ",
           mu_v, " per lembar (konstanta). Nilai ",
           ifelse(mu_v > 0, "positif mengindikasikan kecenderungan kenaikan harga rata-rata.",
                  "negatif mengindikasikan kecenderungan penurunan harga rata-rata."), "\n\n"))
  • \(\mu\) = -0.0319: Rata-rata perubahan harga harian GOTO adalah Rp -0.0319 per lembar (konstanta). Nilai negatif mengindikasikan kecenderungan penurunan harga rata-rata.
if (p_final >= 1) {
  cat(paste0("- **AR(1) = ", ar1_v, "**: Perubahan harga hari ini dipengaruhi oleh perubahan ",
             "harga kemarin sebesar ", ar1_v, " kali. Nilai ",
             ifelse(ar1_v > 0, "positif menandakan **momentum** — kenaikan kemarin cenderung diikuti kenaikan hari ini.",
                    "negatif menandakan **mean-reversion** — kenaikan kemarin cenderung dikoreksi hari ini."), "\n\n"))
}
  • AR(1) = -0.0036: Perubahan harga hari ini dipengaruhi oleh perubahan harga kemarin sebesar -0.0036 kali. Nilai negatif menandakan mean-reversion — kenaikan kemarin cenderung dikoreksi hari ini.
cat("**Interpretasi Persamaan Varians:**\n\n")

Interpretasi Persamaan Varians:

cat(paste0("- **$\\omega$ = ", omega_v, "**: Tingkat varians *baseline* (jangka panjang) ",
           "ketika tidak ada guncangan dari periode sebelumnya. Ini adalah \"lantai\" volatilitas GOTO.\n\n"))
  • \(\omega\) = 8.1201: Tingkat varians baseline (jangka panjang) ketika tidak ada guncangan dari periode sebelumnya. Ini adalah “lantai” volatilitas GOTO.
for (j in seq_along(alpha_vals)) {
  cat(paste0("- **$\\alpha_", j, "$ = ", alpha_vals[j], "**: Guncangan (*shock*) sebesar 1 unit ",
             "pada ", j, " periode lalu meningkatkan varians saat ini sebesar ", alpha_vals[j],
             " unit. Semakin besar $\\alpha_", j, "$, semakin reaktif volatilitas terhadap berita/guncangan.\n\n"))
}
  • \(\alpha_1\) = 0.3089: Guncangan (shock) sebesar 1 unit pada 1 periode lalu meningkatkan varians saat ini sebesar 0.3089 unit. Semakin besar \(\alpha_1\), semakin reaktif volatilitas terhadap berita/guncangan.

  • \(\alpha_2\) = 0.0671: Guncangan (shock) sebesar 1 unit pada 2 periode lalu meningkatkan varians saat ini sebesar 0.0671 unit. Semakin besar \(\alpha_2\), semakin reaktif volatilitas terhadap berita/guncangan.

  • \(\alpha_3\) = 0.5066: Guncangan (shock) sebesar 1 unit pada 3 periode lalu meningkatkan varians saat ini sebesar 0.5066 unit. Semakin besar \(\alpha_3\), semakin reaktif volatilitas terhadap berita/guncangan.

  • \(\alpha_4\) = 0.0177: Guncangan (shock) sebesar 1 unit pada 4 periode lalu meningkatkan varians saat ini sebesar 0.0177 unit. Semakin besar \(\alpha_4\), semakin reaktif volatilitas terhadap berita/guncangan.

  • \(\alpha_5\) = 0.0987: Guncangan (shock) sebesar 1 unit pada 5 periode lalu meningkatkan varians saat ini sebesar 0.0987 unit. Semakin besar \(\alpha_5\), semakin reaktif volatilitas terhadap berita/guncangan.

cat(paste0("- **Persistensi volatilitas** ($\\sum \\alpha_i$ = ",
           round(persistensi, 4), "):\n\n"))
  • Persistensi volatilitas (\(\sum \alpha_i\) = 0.999):
if (persistensi < 1) {
  hl <- -log(2) / log(persistensi)
  cat(paste0("  Persistensi < 1 → varians **stasioner** (akan kembali ke rata-rata).\n\n"))
  cat(paste0("  Varians jangka panjang (*unconditional variance*) = $\\frac{\\omega}{1-\\sum\\alpha_i}$ = $\\frac{",
             omega_v, "}{1-", round(persistensi,4), "}$ = **",
             round(vu_long, 4), "**\n\n"))
  cat(paste0("  Artinya, volatilitas GOTO cenderung stabil dalam jangka panjang di sekitar $\\sigma$ = **",
             round(sqrt(vu_long), 4), "** Rp.\n\n"))
} else {
  cat(paste0("  Persistensi ≥ 1 → varians **tidak stasioner** dalam varians ",
             "(IGARCH — guncangan memiliki efek permanen).\n\n"))
}

Persistensi < 1 → varians stasioner (akan kembali ke rata-rata).

Varians jangka panjang (unconditional variance) = \(\frac{\omega}{1-\sum\alpha_i}\) = \(\frac{8.1201}{1-0.999}\) = 8120.1

Artinya, volatilitas GOTO cenderung stabil dalam jangka panjang di sekitar \(\sigma\) = 90.1116 Rp.

cat("**Implikasi untuk Investor:**\n\n")

Implikasi untuk Investor:

cat(paste0("- Investor dengan profil **risk taker**: dapat memanfaatkan periode volatilitas tinggi ",
           "untuk *scalping* (trading jangka pendek) karena peluang keuntungan besar dalam waktu singkat.\n\n"))
  • Investor dengan profil risk taker: dapat memanfaatkan periode volatilitas tinggi untuk scalping (trading jangka pendek) karena peluang keuntungan besar dalam waktu singkat.
cat(paste0("- Investor dengan profil **risk averse**: sebaiknya menghindari saham GOTO pada periode ",
           "volatilitas tinggi dan mempertimbangkan masuk saat volatilitas kondisional ($\\sigma_t$) ",
           "berada pada level rendah.\n\n"))
  • Investor dengan profil risk averse: sebaiknya menghindari saham GOTO pada periode volatilitas tinggi dan mempertimbangkan masuk saat volatilitas kondisional (\(\sigma_t\)) berada pada level rendah.
cat("- Model ARCH hanya menangkap pola historis volatilitas. Untuk keputusan investasi yang komprehensif, ")
  • Model ARCH hanya menangkap pola historis volatilitas. Untuk keputusan investasi yang komprehensif,
cat("perlu dikombinasikan dengan analisis fundamental (laporan keuangan, prospek bisnis) dan ")

perlu dikombinasikan dengan analisis fundamental (laporan keuangan, prospek bisnis) dan

cat("analisis teknikal (indikator harga).\n")

analisis teknikal (indikator harga).

Tahap 12: Forecasting (Opsional)

horizon <- 252   # ≈ 1 tahun hari kerja

cat(paste0("Melakukan peramalan ", horizon, " hari ke depan...\n"))
#> Melakukan peramalan 252 hari ke depan...
forecast_arch <- ugarchforecast(model_arch, n.ahead = horizon)

# Ekstrak hasil forecast
sigma_fore    <- as.numeric(sigma(forecast_arch))
mean_fore     <- as.numeric(fitted(forecast_arch))
varians_fore  <- sigma_fore^2

# Hitung harga forecast (akumulasi 1st difference)
harga_terakhir <- tail(na.omit(harga), 1)
harga_fore     <- harga_terakhir + cumsum(mean_fore)

# Buat tanggal hari kerja ke depan
tgl_terakhir <- tail(na.omit(data_goto$Date), 1)
tgl_semua    <- seq(tgl_terakhir + 1, by = "day", length.out = ceiling(horizon * 1.5))
hari_libur   <- c("Saturday","Sunday","Sabtu","Minggu")
tgl_kerja    <- tgl_semua[!weekdays(tgl_semua) %in% hari_libur][1:horizon]

Ringkasan Hasil Forecast

df_fc_summary <- data.frame(
  Keterangan = c(
    "Harga terakhir (aktual)",
    "Estimasi harga akhir periode forecast",
    "Perubahan estimasi harga (%)",
    "Volatilitas rata-rata (σ) forecast",
    "Volatilitas tertinggi (σ) forecast",
    "Volatilitas terendah (σ) forecast",
    "Varians rata-rata (σ²) forecast"
  ),
  Nilai = c(
    paste0("Rp ", harga_terakhir),
    paste0("Rp ", round(tail(harga_fore, 1), 2)),
    paste0(round((tail(harga_fore,1) - harga_terakhir)/harga_terakhir*100, 2), "%"),
    round(mean(sigma_fore), 4),
    round(max(sigma_fore),  4),
    round(min(sigma_fore),  4),
    round(mean(varians_fore),4)
  )
)

kable(df_fc_summary,
      caption  = paste0("Ringkasan Hasil Forecast ARCH(", p_arch_terpilih,
                        ") — ", horizon, " Hari Ke Depan"),
      booktabs = TRUE, row.names = FALSE,
      align    = c("l","r")) |>
  kable_styling(latex_options = c("hold_position","striped"), full_width = FALSE)
Ringkasan Hasil Forecast ARCH(5) — 252 Hari Ke Depan
Keterangan Nilai
Harga terakhir (aktual) Rp 68
Estimasi harga akhir periode forecast Rp 59.96
Perubahan estimasi harga (%) -11.83%
Volatilitas rata-rata (σ) forecast 18.9069
Volatilitas tertinggi (σ) forecast 27.8377
Volatilitas terendah (σ) forecast 3.254
Varians rata-rata (σ²) forecast 398.0102

Grafik Forecast Harga

df_aktual  <- data.frame(Tanggal = data_goto$Date, Harga = harga, Tipe = "Aktual")
df_ramal   <- data.frame(Tanggal = tgl_kerja, Harga = harga_fore, Tipe = "Forecast")
df_gabung  <- rbind(df_aktual, df_ramal)

ggplot(df_gabung, aes(x = Tanggal, y = Harga, color = Tipe, linetype = Tipe)) +
  geom_line(linewidth = 0.7) +
  geom_vline(xintercept = as.numeric(tgl_terakhir),
             linetype = "dotted", color = "gray40") +
  scale_color_manual(values = c("Aktual" = "navy", "Forecast" = "darkorange")) +
  scale_linetype_manual(values = c("Aktual" = "solid", "Forecast" = "dashed")) +
  annotate("text", x = tgl_terakhir, y = max(harga)*0.9,
           label = "Akhir Data", hjust = 1.1, size = 3, color = "gray40") +
  labs(title    = paste0("Harga Saham GOTO: Aktual & Forecast ARCH(",
                         p_arch_terpilih, ")"),
       subtitle = paste0("Forecast ", horizon, " hari ke depan (≈ 1 tahun)"),
       x = "Tanggal", y = "Harga (Rp)",
       color = NULL, linetype = NULL) +
  theme_bw(base_size = 10) +
  theme(plot.title = element_text(face = "bold"),
        legend.position = "bottom")
Forecast Harga Saham GOTO — Model ARCH

Forecast Harga Saham GOTO — Model ARCH

Grafik Forecast Volatilitas

df_hist_sig <- data.frame(Tanggal = data_goto$Date[-1],
                          Sigma   = as.numeric(sigma(model_arch)),
                          Tipe    = "Historical")
df_fore_sig <- data.frame(Tanggal = tgl_kerja,
                          Sigma   = sigma_fore,
                          Tipe    = "Forecast")
df_sigma_all <- rbind(df_hist_sig, df_fore_sig)

ggplot(df_sigma_all, aes(x = Tanggal, y = Sigma, color = Tipe, linetype = Tipe)) +
  geom_line(linewidth = 0.7) +
  geom_vline(xintercept = as.numeric(tgl_terakhir),
             linetype = "dotted", color = "gray40") +
  scale_color_manual(values = c("Historical" = "gray50", "Forecast" = "firebrick")) +
  scale_linetype_manual(values = c("Historical" = "solid", "Forecast" = "solid")) +
  labs(title    = paste0("Volatilitas Kondisional GOTO: Historical & Forecast ARCH(",
                         p_arch_terpilih, ")"),
       subtitle = "σ_t tinggi = periode ketidakpastian harga tinggi",
       x = "Tanggal", y = "Conditional Std. Dev. (σ_t)",
       color = NULL, linetype = NULL) +
  theme_bw(base_size = 10) +
  theme(plot.title = element_text(face = "bold"),
        legend.position = "bottom")
Forecast Volatilitas Kondisional GOTO — Model ARCH

Forecast Volatilitas Kondisional GOTO — Model ARCH

Interpretasi Forecast:

Berdasarkan model ARCH(5) yang telah diestimasi, estimasi harga akhir periode forecast adalah Rp 59.96 per lembar. Volatilitas kondisional pada periode forecast menunjukkan tren yang meningkat dibandingkan periode awal forecast, menandakan ketidakpastian yang semakin besar ke depan. Perlu diingat bahwa model ARCH hanya menggunakan informasi historis harga dan tidak dapat mengantisipasi kejadian luar biasa (black swan events) atau perubahan fundamental perusahaan.

Kesimpulan

Berdasarkan analisis yang telah dilakukan secara sistematis mengikuti 12 tahap metodologi ARCH, dapat ditarik kesimpulan sebagai berikut:

  1. Stasioneritas: Data harga penutupan saham GOTO tidak stasioner pada level, tetapi stasioner pada 1st Difference (d = 1), dikonfirmasi oleh uji ADF dengan p-value sangat kecil (< 0,05).

  2. Model Mean Terbaik: Berdasarkan analisis ACF/PACF dan perbandingan satu per satu seluruh kandidat model, ARIMA(1,1,0) dipilih sebagai model mean terbaik dengan AIC = 3323.286. Model ini menghasilkan residual yang bersifat white noise (tidak ada autokorelasi tersisa).

  3. Efek ARCH: Uji ARCH-LM menunjukkan p-value yang sangat signifikan (< 0,05), membuktikan adanya heteroskedastisitas bersyarat pada residual ARIMA. Ini mengkonfirmasi bahwa volatilitas saham GOTO tidak konstan dan dipengaruhi oleh guncangan periode sebelumnya.

  4. Model ARCH Terpilih: ARCH(5) dipilih berdasarkan kombinasi hasil ACF residual kuadrat dan AIC/BIC terkecil. Model ini berhasil menangkap pola volatilitas kondisional GOTO dengan seluruh parameter yang signifikan.

  5. Karakteristik Volatilitas: Nilai persistensi volatilitas \(\alpha_1\) = 0.3089 menunjukkan bahwa guncangan pada harga saham GOTO memiliki pengaruh moderat dan relatif cepat mereda terhadap volatilitas berikutnya.

  6. Rekomendasi Investor: Investor risk taker dapat memanfaatkan periode volatilitas tinggi untuk strategi scalping. Investor risk averse disarankan masuk di periode volatilitas kondisional rendah.

  7. Keterbatasan: Model ARCH hanya berbasis data historis harga dan tidak dapat mengakomodasi faktor eksternal seperti kondisi makroekonomi, kebijakan perusahaan, atau sentimen pasar. Analisis sebaiknya dilengkapi dengan analisis fundamental dan teknikal.


Analisis ini merupakan replikasi dan pengembangan dari: Nugroho, N.W. & Dewi, V.I. (2024). Analisis Forecasting Volatilitas Saham PT Goto Gojek Tokopedia Dengan Metode ARCH-GARCH. INOBIS: Jurnal Inovasi Bisnis dan Manajemen Indonesia, 8(1), 48–57.