1. Pendahuluan

Analisis ini bertujuan melakukan peramalan (forecasting) harga saham menggunakan metode ARIMA (Autoregressive Integrated Moving Average) dengan pendekatan Box-Jenkins. Data yang digunakan adalah harga penutupan (Price) saham Nippon Television Holdings Inc.

Alur analisis yang dilakukan meliputi: pemuatan & pembersihan data, pembagian data training/testing, eksplorasi data, uji stasioneritas dalam varian (Box-Cox), uji stasioneritas dalam mean (ADF), differencing, identifikasi model melalui ACF/PACF, pembentukan dan estimasi kandidat model ARIMA, uji signifikansi parameter, verifikasi asumsi residual, pemilihan model terbaik, peramalan pada data testing, hingga evaluasi akurasi peramalan.

Variabel Keterangan Peran
Date Tanggal perdagangan Indeks waktu
Price Harga penutupan (closing) Variabel yang dianalisis (Y)

2. Data dan Prapemrosesan

2.1 Pemuatan Data

df <- read.csv("C:/Users/ACER/Downloads/Nippon Television Holdings Inc Stock Price History.csv", stringsAsFactors = FALSE)

# Parse tanggal (format asli: MM/DD/YYYY)
df$Date <- as.Date(df$Date, format = "%m/%d/%Y")

# Bersihkan kolom Price: hapus koma ribuan, misal '3,758.0' -> 3758.0
df$Price <- as.numeric(gsub(",", "", df$Price))

# Urutkan ascending berdasarkan tanggal (data terlama di atas)
df <- df[order(df$Date), ]
rownames(df) <- NULL

price <- df$Price
dates <- df$Date

cat(sprintf("Total observasi : %d\n", length(price)))
## Total observasi : 181
cat(sprintf("Periode         : %s  s.d.  %s\n", min(dates), max(dates)))
## Periode         : 2025-08-14  s.d.  2026-05-15

2.2 Tampilan Data

data.frame(Tanggal = format(dates, "%d-%m-%Y"), Price = price) %>%
  head(10) %>%
  tabel_gaya(
    caption = "Tabel 1. Sepuluh Observasi Pertama (Setelah Diurutkan)",
    col.names = c("Tanggal", "Harga Penutupan (Price, JPY)")
  )
Tabel 1. Sepuluh Observasi Pertama (Setelah Diurutkan)
Tanggal Harga Penutupan (Price, JPY)
14-08-2025 3758
15-08-2025 3732
18-08-2025 3768
19-08-2025 3904
20-08-2025 3857
21-08-2025 3883
22-08-2025 3919
25-08-2025 3878
26-08-2025 3865
27-08-2025 3873

2.3 Statistik Deskriptif

deskriptif <- data.frame(
  Statistik = c("Minimum", "Kuartil 1", "Median", "Rata-rata", "Kuartil 3", "Maksimum", "Standar Deviasi"),
  Nilai = round(c(
    min(price), quantile(price, 0.25), median(price), mean(price),
    quantile(price, 0.75), max(price), sd(price)
  ), 2)
)

deskriptif %>%
  tabel_gaya(caption = "Tabel 2. Statistik Deskriptif Harga Saham (Price)")
Tabel 2. Statistik Deskriptif Harga Saham (Price)
Statistik Nilai
Minimum 2919.50
Kuartil 1 3249.00
Median 3758.00
Rata-rata 3611.39
Kuartil 3 3865.00
Maksimum 4146.00
Standar Deviasi 339.88

3. Split Data Training dan Testing (80:20)

N_TOTAL <- length(price)
N_TRAIN <- round(N_TOTAL * 0.8)
N_TEST  <- N_TOTAL - N_TRAIN

train       <- price[1:N_TRAIN]
test        <- price[(N_TRAIN + 1):(N_TRAIN + N_TEST)]
train_dates <- dates[1:N_TRAIN]
test_dates  <- dates[(N_TRAIN + 1):(N_TRAIN + N_TEST)]

data.frame(
  Keterangan = c("Total Data", "Data Training (80%)", "Data Testing (20%)"),
  Jumlah_Observasi = c(N_TOTAL, N_TRAIN, N_TEST),
  Periode_Mulai = c(format(min(dates), "%d-%m-%Y"), format(min(train_dates), "%d-%m-%Y"), format(min(test_dates), "%d-%m-%Y")),
  Periode_Akhir = c(format(max(dates), "%d-%m-%Y"), format(max(train_dates), "%d-%m-%Y"), format(max(test_dates), "%d-%m-%Y"))
) %>%
  tabel_gaya(
    caption = "Tabel 3. Pembagian Data Training dan Testing",
    col.names = c("Keterangan", "Jumlah Observasi", "Periode Mulai", "Periode Akhir")
  )
Tabel 3. Pembagian Data Training dan Testing
Keterangan Jumlah Observasi Periode Mulai Periode Akhir
Total Data 181 14-08-2025 15-05-2026
Data Training (80%) 145 14-08-2025 19-03-2026
Data Testing (20%) 36 23-03-2026 15-05-2026

4. Eksplorasi Data (EDA)

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

plot(dates, price, type = "l", col = warna_utama, lwd = 1.2,
     main = "Time Series Plot - Harga Saham (Price)",
     xlab = "Tanggal", ylab = "Price (JPY)")
abline(v = as.numeric(max(train_dates)), col = warna_tersier, lty = 2, lwd = 1.5)
legend("topleft", legend = c("Price", "Batas Train/Test"),
       col = c(warna_utama, warna_tersier), lty = c(1, 2), lwd = c(1.2, 1.5), bty = "n")
grid(col = "gray90")

plot(train_dates, train, type = "l", col = warna_utama, lwd = 1.2,
     xlim = range(dates), ylim = range(price),
     main = "Pembagian Data Training dan Testing",
     xlab = "Tanggal", ylab = "Price (JPY)")
lines(test_dates, test, col = warna_aksen, lwd = 1.5)
legend("topleft",
       legend = c(sprintf("Training (%d data)", N_TRAIN), sprintf("Testing (%d data)", N_TEST)),
       col = c(warna_utama, warna_aksen), lty = 1, lwd = c(1.2, 1.5), bty = "n")
grid(col = "gray90")

par(mfrow = c(1, 1))

Dari plot di atas terlihat pola pergerakan harga saham sepanjang periode pengamatan, termasuk tren naik/turun yang menjadi dasar dilakukannya uji stasioneritas pada tahap berikutnya.


5. Uji Stasioneritas dalam Varian (Box-Cox)

Uji Box-Cox (metode Guerrero via BoxCox.lambda()) digunakan untuk memeriksa apakah data memiliki varian yang stabil sepanjang waktu.

ts_train <- ts(train)

lam_estimated <- BoxCox.lambda(ts_train)
lam_rounded   <- round(lam_estimated)

if (lam_rounded == 1) {
  train_trans <- train
  lam_used    <- 1
  status_varian <- "Sudah stasioner dalam varian (tidak perlu transformasi)"
} else {
  train_trans <- BoxCox(train, lam_estimated)
  lam_used    <- lam_estimated
  status_varian <- "Belum stasioner, dilakukan transformasi Box-Cox"
}

data.frame(
  Statistik = c("Estimated Lambda (\u03bb)", "Rounded Value", "Status"),
  Nilai = c(round(lam_estimated, 4), lam_rounded, status_varian)
) %>%
  tabel_gaya(caption = "Tabel 4. Hasil Uji Box-Cox")
Tabel 4. Hasil Uji Box-Cox
Statistik Nilai
Estimated Lambda (λ) 0.7757
Rounded Value 1
Status Sudah stasioner dalam varian (tidak perlu transformasi)
par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))

plot(train_dates, train, type = "l", col = warna_utama, lwd = 1,
     main = "Data Asli", xlab = "Tanggal", ylab = "Price")
grid(col = "gray90")

judul_transformasi <- if (lam_rounded == 1) {
  "Data Digunakan\n(Tanpa Transformasi)"
} else {
  sprintf("Setelah Box-Cox\n(\u03bb = %.4f)", lam_estimated)
}

plot(train_dates, train_trans, type = "l", col = warna_aksen, lwd = 1,
     main = judul_transformasi, xlab = "Tanggal", ylab = "Transformed Price")
grid(col = "gray90")

par(mfrow = c(1, 1))

Nilai λ terestimasi sebesar 0.7757 (dibulatkan menjadi 1), sehingga data sudah stasioner dalam varian dan tidak memerlukan transformasi.


6. Uji Stasioneritas dalam Mean (ADF) — Sebelum Differencing

uji_adf <- function(series) {
  res <- adf.test(na.omit(series))
  list(statistic = as.numeric(res$statistic), pvalue = res$p.value, lag = res$parameter)
}

adf_before <- uji_adf(train_trans)
data.frame(
  Statistik = c("ADF Statistic", "p-value", "Lag", "Kesimpulan"),
  Nilai = c(
    round(adf_before$statistic, 4),
    format.pval(adf_before$pvalue, digits = 4),
    adf_before$lag,
    ifelse(adf_before$pvalue < 0.05, "Stasioner dalam mean", "Tidak stasioner - perlu differencing")
  )
) %>%
  tabel_gaya(caption = "Tabel 5. Hasil Uji ADF Sebelum Differencing")
Tabel 5. Hasil Uji ADF Sebelum Differencing
Statistik Nilai
ADF Statistic -1.1326
p-value 0.9143
Lag 5
Kesimpulan Tidak stasioner - perlu differencing
par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))

plot(train_dates, train_trans, type = "l", col = warna_utama, lwd = 1,
     main = "Time Series Plot - Sebelum Differencing",
     xlab = "Tanggal", ylab = "Transformed Price")
abline(h = mean(train_trans), col = warna_tersier, lty = 2)
legend("topleft", legend = "Mean", col = warna_tersier, lty = 2, bty = "n")
grid(col = "gray90")

x  <- seq_along(train_trans)
cf <- lm(train_trans ~ x)
plot(x, train_trans, type = "l", col = warna_utama, lwd = 1,
     main = "Trend Analysis - Sebelum Differencing",
     xlab = "Indeks", ylab = "Transformed Price")
abline(cf, col = warna_tersier, lty = 2, lwd = 2)
legend("topleft", legend = sprintf("Trend (slope=%.4f)", coef(cf)[2]), col = warna_tersier, lty = 2, bty = "n")
grid(col = "gray90")

par(mfrow = c(1, 1))

7. Differencing dan ADF Setelah Differencing

train_diff       <- diff(train_trans)
train_diff_dates <- train_dates[-1]

adf_after <- uji_adf(train_diff)

d <- ifelse(adf_before$pvalue < 0.05, 0, 1)
data.frame(
  Statistik = c("ADF Statistic", "p-value", "Lag", "Kesimpulan", "Orde Differencing (d) Terpilih"),
  Nilai = c(
    round(adf_after$statistic, 4),
    format.pval(adf_after$pvalue, digits = 4),
    adf_after$lag,
    ifelse(adf_after$pvalue < 0.05, "Stasioner dalam mean", "Masih tidak stasioner"),
    d
  )
) %>%
  tabel_gaya(caption = "Tabel 6. Hasil Uji ADF Setelah Differencing Orde 1")
Tabel 6. Hasil Uji ADF Setelah Differencing Orde 1
Statistik Nilai
ADF Statistic -5.1107
p-value 0.01
Lag 5
Kesimpulan Stasioner dalam mean
Orde Differencing (d) Terpilih 1
par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))

plot(train_diff_dates, train_diff, type = "l", col = warna_aksen, lwd = 1,
     main = "Time Series Plot - Setelah Differencing (d=1)",
     xlab = "Tanggal", ylab = "Differenced Price")
abline(h = mean(train_diff), col = warna_tersier, lty = 2)
legend("topleft", legend = "Mean", col = warna_tersier, lty = 2, bty = "n")
grid(col = "gray90")

x2  <- seq_along(train_diff)
cf2 <- lm(train_diff ~ x2)
plot(x2, train_diff, type = "l", col = warna_aksen, lwd = 1,
     main = "Trend Analysis - Setelah Differencing",
     xlab = "Indeks", ylab = "Differenced Price")
abline(cf2, col = warna_tersier, lty = 2, lwd = 2)
legend("topleft", legend = sprintf("Trend (slope=%.6f)", coef(cf2)[2]), col = warna_tersier, lty = 2, bty = "n")
grid(col = "gray90")

par(mfrow = c(1, 1))

Karena data belum stasioner dalam mean sebelum differencing, dilakukan differencing orde 1. Setelah differencing, hasil uji ADF menunjukkan data menjadi stasioner, sehingga orde differencing yang digunakan pada model ARIMA adalah d = 1.


8. Plot ACF dan PACF

series_acf <- if (d == 1) train_diff else train_trans
judul_acf  <- if (d == 1) "ACF - Data Setelah Differencing (d=1)" else "ACF - Data Asli (Sudah Stasioner, d=0)"
judul_pacf <- if (d == 1) "PACF - Data Setelah Differencing (d=1)" else "PACF - Data Asli (Sudah Stasioner, d=0)"
par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))

acf(series_acf, lag.max = 20, col = warna_utama, main = judul_acf)
grid(col = "gray90")

pacf(series_acf, lag.max = 20, col = warna_aksen, main = judul_pacf)
grid(col = "gray90")

par(mfrow = c(1, 1))
acf_val  <- acf(series_acf,  lag.max = 20, plot = FALSE)
pacf_val <- pacf(series_acf, lag.max = 20, plot = FALSE)

n         <- length(series_acf)
batas_sig <- 1.96 / sqrt(n)

lag_acf_sig  <- which(abs(acf_val$acf[-1]) > batas_sig)
lag_pacf_sig <- which(abs(pacf_val$acf)    > batas_sig)

data.frame(
  Lag  = 1:20,
  ACF  = round(as.numeric(acf_val$acf[-1]), 4),
  Signifikan_ACF = abs(as.numeric(acf_val$acf[-1])) > batas_sig,
  PACF = round(as.numeric(pacf_val$acf), 4),
  Signifikan_PACF = abs(as.numeric(pacf_val$acf)) > batas_sig
) %>%
  tabel_gaya(
    caption = "Tabel 7. Nilai ACF dan PACF per Lag (1-20)",
    col.names = c("Lag", "ACF", "Signifikan (ACF)", "PACF", "Signifikan (PACF)")
  )
Tabel 7. Nilai ACF dan PACF per Lag (1-20)
Lag ACF Signifikan (ACF) PACF Signifikan (PACF)
1 -0.1900 TRUE -0.1900 TRUE
2 0.0608 FALSE 0.0257 FALSE
3 -0.0825 FALSE -0.0689 FALSE
4 -0.0962 FALSE -0.1306 FALSE
5 0.0568 FALSE 0.0216 FALSE
6 0.0810 FALSE 0.1035 FALSE
7 0.0278 FALSE 0.0431 FALSE
8 -0.0831 FALSE -0.0890 FALSE
9 0.0922 FALSE 0.0900 FALSE
10 -0.0924 FALSE -0.0290 FALSE
11 0.0194 FALSE -0.0314 FALSE
12 0.0497 FALSE 0.0413 FALSE
13 0.0138 FALSE 0.0488 FALSE
14 0.1102 FALSE 0.1160 FALSE
15 -0.0831 FALSE -0.0519 FALSE
16 0.0379 FALSE 0.0261 FALSE
17 -0.0071 FALSE 0.0538 FALSE
18 0.0336 FALSE 0.0230 FALSE
19 0.0016 FALSE -0.0128 FALSE
20 -0.1579 FALSE -0.1818 TRUE
data.frame(
  Statistik = c("Batas Signifikansi (\u00b1)", "Lag ACF Signifikan (kandidat q)", "Lag PACF Signifikan (kandidat p)"),
  Nilai = c(
    round(batas_sig, 4),
    ifelse(length(lag_acf_sig) > 0, paste(lag_acf_sig, collapse = ", "), "Tidak ada"),
    ifelse(length(lag_pacf_sig) > 0, paste(lag_pacf_sig, collapse = ", "), "Tidak ada")
  )
) %>%
  tabel_gaya(caption = "Tabel 8. Ringkasan Lag Signifikan ACF dan PACF")
Tabel 8. Ringkasan Lag Signifikan ACF dan PACF
Statistik Nilai
Batas Signifikansi (±) 0.1633
Lag ACF Signifikan (kandidat q) 1
Lag PACF Signifikan (kandidat p) 1, 20

Panduan pembacaan: ACF terpotong di lag k mengindikasikan komponen MA(k) (nilai q), sedangkan PACF terpotong di lag k mengindikasikan komponen AR(k) (nilai p). Nilai-nilai ini menjadi dasar pembentukan kandidat model ARIMA pada tahap berikutnya.


9. Pemodelan ARIMA

9.1 Pembentukan Kandidat Model

Kandidat model ARIMA dibentuk secara otomatis dari kombinasi lag signifikan ACF (kandidat q) dan PACF (kandidat p), termasuk kombinasi lag musiman/tidak berurutan menggunakan argumen fixed.

p_lags  <- lag_pacf_sig[lag_pacf_sig <= 20]
q_lags  <- lag_acf_sig[lag_acf_sig   <= 20]
p_range <- unique(c(0, p_lags))
q_range <- unique(c(0, q_lags))

kandidat <- list()

# 1) Kombinasi individual p dan q
for (p_val in p_range) {
  for (q_val in q_range) {
    if (p_val == 0 && q_val == 0) next
    kandidat <- c(kandidat, list(list(
      order = c(p_val, d, q_val),
      fixed = NULL,
      label = sprintf("ARIMA(%d,%d,%d)", p_val, d, q_val)
    )))
  }
}

# 2) Kombinasi lag PACF sekaligus: ARIMA([lag1,lag2],d,q)
if (length(p_lags) > 1) {
  p_max    <- max(p_lags)
  fixed_ar <- rep(0, p_max)
  for (lg in p_lags) fixed_ar[lg] <- NA
  for (q_val in q_range) {
    fixed_full <- c(fixed_ar, if (q_val > 0) rep(NA, q_val) else NULL)
    label      <- sprintf("ARIMA([%s],%d,%d)", paste(p_lags, collapse = ","), d, q_val)
    kandidat   <- c(kandidat, list(list(order = c(p_max, d, q_val), fixed = fixed_full, label = label)))
  }
}

# 3) Kombinasi lag ACF sekaligus: ARIMA(p,d,[lag1,lag2])
if (length(q_lags) > 1) {
  q_max    <- max(q_lags)
  fixed_ma <- rep(0, q_max)
  for (lg in q_lags) fixed_ma[lg] <- NA
  for (p_val in p_range) {
    fixed_full <- c(if (p_val > 0) rep(NA, p_val) else NULL, fixed_ma)
    label      <- sprintf("ARIMA(%d,%d,[%s])", p_val, d, paste(q_lags, collapse = ","))
    kandidat   <- c(kandidat, list(list(order = c(p_val, d, q_max), fixed = fixed_full, label = label)))
  }
}

data.frame(
  No = seq_along(kandidat),
  Model = sapply(kandidat, function(x) x$label)
) %>%
  tabel_gaya(caption = sprintf("Tabel 9. Daftar %d Model Kandidat ARIMA", length(kandidat)))
Tabel 9. Daftar 7 Model Kandidat ARIMA
No Model
1 ARIMA(0,1,1)
2 ARIMA(1,1,0)
3 ARIMA(1,1,1)
4 ARIMA(20,1,0)
5 ARIMA(20,1,1)
6 ARIMA([1,20],1,0)
7 ARIMA([1,20],1,1)

9.2 Estimasi Seluruh Model Kandidat

Setiap kandidat diestimasi dua kali (dengan dan tanpa konstanta). Jika d > 0, konstanta direpresentasikan sebagai drift; jika d = 0, konstanta direpresentasikan sebagai mean.

results_list  <- list()
results_names <- c()
ts_train_trans <- ts(train_trans)

for (kand in kandidat) {
  for (inc_const in c(TRUE, FALSE)) {

    konst    <- ifelse(inc_const, "Dengan Konstanta", "Tanpa Konstanta")
    key_full <- sprintf("%s_%s", kand$label, konst)

    if (d > 0) {
      v_include.mean  <- FALSE
      v_include.drift <- inc_const
    } else {
      v_include.mean  <- inc_const
      v_include.drift <- FALSE
    }

    v_fixed <- kand$fixed
    if (!is.null(v_fixed) && inc_const) {
      v_fixed <- c(v_fixed, NA)
    }

    res <- tryCatch({
      suppressWarnings(
        Arima(ts_train_trans,
              order         = kand$order,
              fixed         = v_fixed,
              include.mean  = v_include.mean,
              include.drift = v_include.drift)
      )
    }, error = function(e) NULL)

    if (is.null(res)) next
    if (any(is.nan(coef(res))) || any(is.na(coef(res)))) next

    results_list[[key_full]]  <- res
    results_names             <- c(results_names, key_full)
  }
}

data.frame(
  Model = results_names,
  AIC = round(sapply(results_list, function(x) x$aic), 2),
  BIC = round(sapply(results_list, function(x) x$bic), 2)
) %>%
  arrange(AIC) %>%
  tabel_gaya(caption = sprintf("Tabel 10. Hasil Estimasi %d Model yang Berhasil Di-fit (Diurutkan Berdasarkan AIC)", length(results_list)))
Tabel 10. Hasil Estimasi 14 Model yang Berhasil Di-fit (Diurutkan Berdasarkan AIC)
Model AIC BIC
ARIMA([1,20],1,0)_Tanpa Konstanta 1586.23 1595.14
ARIMA([1,20],1,0)_Dengan Konstanta 1586.59 1598.47
ARIMA([1,20],1,1)_Tanpa Konstanta 1587.41 1599.28
ARIMA(1,1,0)_Tanpa Konstanta 1587.90 1593.84
ARIMA([1,20],1,1)_Dengan Konstanta 1588.03 1602.87
ARIMA(0,1,1)_Tanpa Konstanta 1588.23 1594.17
ARIMA(1,1,0)_Dengan Konstanta 1588.79 1597.70
ARIMA(0,1,1)_Dengan Konstanta 1589.06 1597.97
ARIMA(1,1,1)_Tanpa Konstanta 1589.68 1598.59
ARIMA(1,1,1)_Dengan Konstanta 1590.63 1602.51
ARIMA(20,1,0)_Tanpa Konstanta 1608.69 1671.06
ARIMA(20,1,0)_Dengan Konstanta 1609.62 1674.95
ARIMA(20,1,1)_Tanpa Konstanta 1609.89 1675.22
ARIMA(20,1,1)_Dengan Konstanta 1610.95 1679.25

10. Uji Signifikansi Parameter (α = 5%)

sig_results <- list()
param_table <- data.frame()

for (key in results_names) {
  res   <- results_list[[key]]
  coefs <- coef(res)

  if (length(coefs) == 0) {
    sig_results[[key]] <- TRUE
    next
  }

  vcov_m <- tryCatch(vcov(res), error = function(e) NULL)

  if (is.null(vcov_m) || any(is.na(diag(vcov_m)))) {
    sig_results[[key]] <- FALSE
    next
  }

  ses     <- sqrt(diag(vcov_m))
  tvals   <- coefs / ses
  pvals   <- 2 * pnorm(abs(tvals), lower.tail = FALSE)
  all_sig <- all(pvals < 0.05)
  sig_results[[key]] <- all_sig

  for (i in seq_along(coefs)) {
    param_table <- rbind(param_table, data.frame(
      Model = key,
      Parameter = names(coefs)[i],
      Estimasi = round(coefs[i], 4),
      SE = round(ses[i], 4),
      p_value = round(pvals[i], 4),
      Signifikan = ifelse(pvals[i] < 0.05, "Ya", "Tidak")
    ))
  }
}

param_table %>%
  tabel_gaya(
    caption = "Tabel 11. Estimasi Parameter dan Uji Signifikansi Seluruh Model Kandidat",
    col.names = c("Model", "Parameter", "Estimasi", "Std. Error", "p-value", "Signifikan?")
  )
Tabel 11. Estimasi Parameter dan Uji Signifikansi Seluruh Model Kandidat
Model Parameter Estimasi Std. Error p-value Signifikan?
ARIMA(0,1,1)_Dengan Konstanta ma1 -0.1851 0.0820 0.0240 Ya
ARIMA(0,1,1)_Dengan Konstanta drift -4.3625 4.0137 0.2771 Tidak
ARIMA(0,1,1)_Tanpa Konstanta ma1 -0.1765 0.0811 0.0295 Ya
ARIMA(1,1,0)_Dengan Konstanta ar1 -0.1910 0.0820 0.0198 Ya
ARIMA(1,1,0)_Dengan Konstanta drift -4.3616 4.1295 0.2909 Tidak
ARIMA(1,1,0)_Tanpa Konstanta ar1 -0.1856 0.0822 0.0239 Ya
ARIMA(1,1,1)_Dengan Konstanta ar1 -0.3651 0.3751 0.3303 Tidak
ARIMA(1,1,1)_Dengan Konstanta ma1 0.1813 0.3957 0.6468 Tidak
ARIMA(1,1,1)_Dengan Konstanta drift -4.3771 4.2529 0.3034 Tidak
ARIMA(1,1,1)_Tanpa Konstanta ar1 -0.3800 0.3594 0.2903 Tidak
ARIMA(1,1,1)_Tanpa Konstanta ma1 0.2020 0.3792 0.5943 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar1 -0.1829 0.0816 0.0249 Ya
ARIMA(20,1,0)_Dengan Konstanta ar2 0.0462 0.0833 0.5794 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar3 -0.0716 0.0843 0.3952 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar4 -0.1410 0.0848 0.0963 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar5 0.0226 0.0853 0.7913 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar6 0.1625 0.0854 0.0572 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar7 0.0067 0.0849 0.9370 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar8 -0.0894 0.0857 0.2971 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar9 0.1019 0.0854 0.2329 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar10 -0.0098 0.0873 0.9103 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar11 -0.0133 0.0856 0.8760 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar12 0.0109 0.0854 0.8980 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar13 0.0859 0.0854 0.3145 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar14 0.1412 0.0864 0.1021 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar15 -0.0541 0.0866 0.5317 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar16 0.0021 0.0886 0.9809 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar17 0.0559 0.0883 0.5265 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar18 0.0409 0.0896 0.6475 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar19 -0.0535 0.0892 0.5491 Tidak
ARIMA(20,1,0)_Dengan Konstanta ar20 -0.2066 0.0865 0.0169 Ya
ARIMA(20,1,0)_Dengan Konstanta drift -4.6573 4.0626 0.2516 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar1 -0.1790 0.0818 0.0287 Ya
ARIMA(20,1,0)_Tanpa Konstanta ar2 0.0521 0.0834 0.5327 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar3 -0.0630 0.0842 0.4539 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar4 -0.1326 0.0847 0.1175 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar5 0.0322 0.0851 0.7055 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar6 0.1714 0.0853 0.0445 Ya
ARIMA(20,1,0)_Tanpa Konstanta ar7 0.0155 0.0847 0.8553 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar8 -0.0802 0.0855 0.3482 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar9 0.1118 0.0851 0.1889 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar10 -0.0007 0.0870 0.9936 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar11 -0.0045 0.0854 0.9578 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar12 0.0219 0.0850 0.7966 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar13 0.0967 0.0850 0.2553 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar14 0.1503 0.0862 0.0812 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar15 -0.0437 0.0862 0.6125 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar16 0.0149 0.0880 0.8659 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar17 0.0687 0.0877 0.4333 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar18 0.0549 0.0888 0.5364 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar19 -0.0394 0.0885 0.6559 Tidak
ARIMA(20,1,0)_Tanpa Konstanta ar20 -0.1952 0.0861 0.0234 Ya
ARIMA(20,1,1)_Dengan Konstanta ar1 -0.4927 0.3173 0.1205 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar2 -0.0109 0.1080 0.9200 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar3 -0.0601 0.0933 0.5197 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar4 -0.1672 0.0966 0.0835 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar5 -0.0223 0.1027 0.8280 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar6 0.1737 0.0943 0.0656 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar7 0.0505 0.1023 0.6213 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar8 -0.0898 0.0942 0.3405 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar9 0.0725 0.0978 0.4580 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar10 0.0234 0.1009 0.8166 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar11 -0.0156 0.0938 0.8675 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar12 0.0000 0.0940 1.0000 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar13 0.0967 0.0942 0.3045 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar14 0.1689 0.0978 0.0841 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar15 -0.0181 0.1017 0.8591 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar16 -0.0145 0.0979 0.8818 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar17 0.0657 0.0976 0.5008 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar18 0.0614 0.1007 0.5422 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar19 -0.0454 0.1000 0.6500 Tidak
ARIMA(20,1,1)_Dengan Konstanta ar20 -0.2097 0.0889 0.0184 Ya
ARIMA(20,1,1)_Dengan Konstanta ma1 0.3254 0.3217 0.3118 Tidak
ARIMA(20,1,1)_Dengan Konstanta drift -4.5554 4.2614 0.2851 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar1 -0.5132 0.3078 0.0955 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar2 -0.0086 0.1076 0.9363 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar3 -0.0491 0.0940 0.6010 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar4 -0.1584 0.0964 0.1005 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar5 -0.0143 0.1019 0.8885 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar6 0.1858 0.0948 0.0501 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar7 0.0654 0.1027 0.5246 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar8 -0.0784 0.0946 0.4074 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar9 0.0824 0.0973 0.3971 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar10 0.0375 0.1014 0.7117 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar11 -0.0046 0.0941 0.9609 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar12 0.0123 0.0941 0.8958 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar13 0.1106 0.0945 0.2418 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar14 0.1830 0.0982 0.0625 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar15 -0.0022 0.1021 0.9830 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar16 -0.0009 0.0976 0.9926 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar17 0.0817 0.0977 0.4030 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar18 0.0798 0.1005 0.4276 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar19 -0.0275 0.1001 0.7838 Tidak
ARIMA(20,1,1)_Tanpa Konstanta ar20 -0.1972 0.0892 0.0270 Ya
ARIMA(20,1,1)_Tanpa Konstanta ma1 0.3499 0.3102 0.2593 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar1 -0.1904 0.0804 0.0179 Ya
ARIMA([1,20],1,0)_Dengan Konstanta ar2 0.0000 0.0841 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar3 0.0000 3.6101 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar4 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar5 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar6 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar7 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar8 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar9 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar10 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar11 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar12 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar13 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar14 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar15 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar16 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar17 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar18 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar19 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Dengan Konstanta ar20 -0.1745 NA 0.0379 Ya
ARIMA([1,20],1,0)_Dengan Konstanta drift -4.6594 NA 0.1968 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar1 -0.1841 0.0808 0.0226 Ya
ARIMA([1,20],1,0)_Tanpa Konstanta ar2 0.0000 0.0841 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar3 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar4 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar5 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar6 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar7 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar8 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar9 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar10 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar11 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar12 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar13 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar14 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar15 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar16 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar17 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar18 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar19 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,0)_Tanpa Konstanta ar20 -0.1630 NA 0.0527 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar1 -0.4569 0.2781 0.1003 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar2 0.0000 0.0770 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar3 0.0000 0.3156 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar4 0.0000 3.9050 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar5 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar6 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar7 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar8 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar9 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar10 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar11 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar12 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar13 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar14 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar15 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar16 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar17 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar18 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar19 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ar20 -0.1634 NA 0.9666 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta ma1 0.2932 NA 0.2918 Tidak
ARIMA([1,20],1,1)_Dengan Konstanta drift -4.6355 NA 0.0000 Ya
ARIMA([1,20],1,1)_Tanpa Konstanta ar1 -0.4834 0.2577 0.0606 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar2 0.0000 0.0749 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar3 0.0000 0.2936 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar4 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar5 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar6 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar7 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar8 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar9 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar10 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar11 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar12 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar13 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar14 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar15 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar16 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar17 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar18 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar19 0.0000 NA 1.0000 Tidak
ARIMA([1,20],1,1)_Tanpa Konstanta ar20 -0.1551 NA 0.0383 Ya
ARIMA([1,20],1,1)_Tanpa Konstanta ma1 0.3282 NA 0.2636 Tidak

11. Verifikasi Model (Uji Diagnostik Residual)

11.1 Normalitas Residual — Kolmogorov-Smirnov

norm_results <- list()
norm_table   <- data.frame()

for (key in results_names) {
  resid_clean <- na.omit(residuals(results_list[[key]]))
  resid_std   <- scale(resid_clean)
  ks <- ks.test(resid_std, "pnorm", mean = 0, sd = 1)
  ok <- ks$p.value > 0.05
  norm_results[[key]] <- ok
  norm_table <- rbind(norm_table, data.frame(
    Model = key, p_value = round(ks$p.value, 4), Kesimpulan = ifelse(ok, "Normal", "Tidak Normal")
  ))
}

norm_table %>%
  tabel_gaya(
    caption = "Tabel 12. Hasil Uji Normalitas Residual (Kolmogorov-Smirnov, \u03b1=5%)",
    col.names = c("Model", "p-value", "Kesimpulan")
  )
Tabel 12. Hasil Uji Normalitas Residual (Kolmogorov-Smirnov, α=5%)
Model p-value Kesimpulan
ARIMA(0,1,1)_Dengan Konstanta 0.4965 Normal
ARIMA(0,1,1)_Tanpa Konstanta 0.5709 Normal
ARIMA(1,1,0)_Dengan Konstanta 0.5709 Normal
ARIMA(1,1,0)_Tanpa Konstanta 0.5506 Normal
ARIMA(1,1,1)_Dengan Konstanta 0.6562 Normal
ARIMA(1,1,1)_Tanpa Konstanta 0.6514 Normal
ARIMA(20,1,0)_Dengan Konstanta 0.5624 Normal
ARIMA(20,1,0)_Tanpa Konstanta 0.7537 Normal
ARIMA(20,1,1)_Dengan Konstanta 0.4288 Normal
ARIMA(20,1,1)_Tanpa Konstanta 0.7097 Normal
ARIMA([1,20],1,0)_Dengan Konstanta 0.5315 Normal
ARIMA([1,20],1,0)_Tanpa Konstanta 0.5965 Normal
ARIMA([1,20],1,1)_Dengan Konstanta 0.6006 Normal
ARIMA([1,20],1,1)_Tanpa Konstanta 0.6683 Normal

11.2 Homogenitas — Ljung-Box pada Kuadrat Residual

homo_results <- list()
homo_table   <- data.frame()
lags_use_h   <- c(12, 24, 36)

for (key in results_names) {
  resid_clean <- na.omit(residuals(results_list[[key]]))
  pvals_lb <- sapply(lags_use_h, function(lg) Box.test(resid_clean^2, lag = lg, type = "Ljung-Box")$p.value)
  ok <- all(pvals_lb > 0.05)
  homo_results[[key]] <- ok
  homo_table <- rbind(homo_table, data.frame(
    Model = key, Min_p_value = round(min(pvals_lb), 4), Kesimpulan = ifelse(ok, "Homogen", "Tidak Homogen")
  ))
}

homo_table %>%
  tabel_gaya(
    caption = "Tabel 13. Hasil Uji Homogenitas Residual (Ljung-Box Kuadrat Residual, \u03b1=5%)",
    col.names = c("Model", "Min p-value", "Kesimpulan")
  )
Tabel 13. Hasil Uji Homogenitas Residual (Ljung-Box Kuadrat Residual, α=5%)
Model Min p-value Kesimpulan
ARIMA(0,1,1)_Dengan Konstanta 0.1367 Homogen
ARIMA(0,1,1)_Tanpa Konstanta 0.1709 Homogen
ARIMA(1,1,0)_Dengan Konstanta 0.1086 Homogen
ARIMA(1,1,0)_Tanpa Konstanta 0.1346 Homogen
ARIMA(1,1,1)_Dengan Konstanta 0.1009 Homogen
ARIMA(1,1,1)_Tanpa Konstanta 0.1240 Homogen
ARIMA(20,1,0)_Dengan Konstanta 0.1784 Homogen
ARIMA(20,1,0)_Tanpa Konstanta 0.1845 Homogen
ARIMA(20,1,1)_Dengan Konstanta 0.2217 Homogen
ARIMA(20,1,1)_Tanpa Konstanta 0.2234 Homogen
ARIMA([1,20],1,0)_Dengan Konstanta 0.0349 Tidak Homogen
ARIMA([1,20],1,0)_Tanpa Konstanta 0.0601 Homogen
ARIMA([1,20],1,1)_Dengan Konstanta 0.0435 Tidak Homogen
ARIMA([1,20],1,1)_Tanpa Konstanta 0.0752 Homogen

11.3 Independensi Residual — Ljung-Box

indep_results <- list()
indep_table   <- data.frame()
lags_use_i    <- c(12, 24, 36, 48)

for (key in results_names) {
  resid_clean <- na.omit(residuals(results_list[[key]]))
  pvals_lb <- sapply(lags_use_i, function(lg) Box.test(resid_clean, lag = lg, type = "Ljung-Box")$p.value)
  ok <- all(pvals_lb > 0.05)
  indep_results[[key]] <- ok
  indep_table <- rbind(indep_table, data.frame(
    Model = key, Min_p_value = round(min(pvals_lb), 4), Kesimpulan = ifelse(ok, "Independen", "Tidak Independen")
  ))
}

indep_table %>%
  tabel_gaya(
    caption = "Tabel 14. Hasil Uji Independensi Residual (Ljung-Box, \u03b1=5%)",
    col.names = c("Model", "Min p-value", "Kesimpulan")
  )
Tabel 14. Hasil Uji Independensi Residual (Ljung-Box, α=5%)
Model Min p-value Kesimpulan
ARIMA(0,1,1)_Dengan Konstanta 0.7760 Independen
ARIMA(0,1,1)_Tanpa Konstanta 0.7716 Independen
ARIMA(1,1,0)_Dengan Konstanta 0.7676 Independen
ARIMA(1,1,0)_Tanpa Konstanta 0.7680 Independen
ARIMA(1,1,1)_Dengan Konstanta 0.7589 Independen
ARIMA(1,1,1)_Tanpa Konstanta 0.7576 Independen
ARIMA(20,1,0)_Dengan Konstanta 0.9999 Independen
ARIMA(20,1,0)_Tanpa Konstanta 0.9999 Independen
ARIMA(20,1,1)_Dengan Konstanta 1.0000 Independen
ARIMA(20,1,1)_Tanpa Konstanta 0.9999 Independen
ARIMA([1,20],1,0)_Dengan Konstanta 0.5744 Independen
ARIMA([1,20],1,0)_Tanpa Konstanta 0.5968 Independen
ARIMA([1,20],1,1)_Dengan Konstanta 0.5598 Independen
ARIMA([1,20],1,1)_Tanpa Konstanta 0.5721 Independen

11.4 Probability Plot (Uji Normalitas Visual)

Probability plot berikut ditampilkan untuk model-model yang lolos uji signifikansi parameter.

lolos_keys <- results_names[sapply(results_names, function(k) isTRUE(sig_results[[k]]))]

if (length(lolos_keys) > 0) {
  ncols <- 2
  nrows <- ceiling(length(lolos_keys) / ncols)
  par(mfrow = c(nrows, ncols), mar = c(4, 4, 3, 1))
  for (key in lolos_keys) {
    resid_clean <- na.omit(residuals(results_list[[key]]))
    qqnorm(resid_clean, main = sprintf("Probability Plot\n%s", substr(key, 1, 35)),
           col = warna_utama, pch = 16, cex = 0.7)
    qqline(resid_clean, col = warna_tersier, lwd = 1.5)
    grid(col = "gray90")
  }
  par(mfrow = c(1, 1))
} else {
  cat("Tidak ada model yang lolos uji signifikansi parameter.\n")
}


12. Pemilihan Model Terbaik (Berdasarkan AIC & MSE)

comparison <- data.frame(
  Model = character(), Sig = logical(), Normal = logical(),
  Homo = logical(), Indep = logical(), AIC = numeric(), MSE = numeric(),
  stringsAsFactors = FALSE
)

for (key in results_names) {
  res    <- results_list[[key]]
  sig    <- isTRUE(sig_results[[key]])
  normal <- isTRUE(norm_results[[key]])
  homo   <- isTRUE(homo_results[[key]])
  indep  <- isTRUE(indep_results[[key]])
  aic    <- res$aic

  fitted_vals <- fitted(res)
  n_fit       <- length(fitted_vals)
  actual_fit  <- tail(train_trans, n_fit)
  mse         <- mean((actual_fit - fitted_vals)^2, na.rm = TRUE)

  comparison <- rbind(comparison, data.frame(
    Model = key, Sig = sig, Normal = normal, Homo = homo, Indep = indep, AIC = aic, MSE = mse,
    stringsAsFactors = FALSE
  ))
}

comparison %>%
  arrange(AIC) %>%
  mutate(across(c(AIC, MSE), ~ round(.x, 4))) %>%
  tabel_gaya(
    caption = "Tabel 15. Tabel Perbandingan Seluruh Model Kandidat",
    col.names = c("Model", "Signifikan", "Normal", "Homogen", "Independen", "AIC", "MSE")
  )
Tabel 15. Tabel Perbandingan Seluruh Model Kandidat
Model Signifikan Normal Homogen Independen AIC MSE
ARIMA([1,20],1,0)_Tanpa Konstanta FALSE TRUE TRUE TRUE 1586.226 3377.528
ARIMA([1,20],1,0)_Dengan Konstanta FALSE TRUE FALSE TRUE 1586.592 3337.406
ARIMA([1,20],1,1)_Tanpa Konstanta FALSE TRUE TRUE TRUE 1587.405 3356.109
ARIMA(1,1,0)_Tanpa Konstanta TRUE TRUE TRUE TRUE 1587.896 3478.190
ARIMA([1,20],1,1)_Dengan Konstanta FALSE TRUE FALSE TRUE 1588.025 3322.949
ARIMA(0,1,1)_Tanpa Konstanta TRUE TRUE TRUE TRUE 1588.226 3486.245
ARIMA(1,1,0)_Dengan Konstanta FALSE TRUE TRUE TRUE 1588.789 3451.502
ARIMA(0,1,1)_Dengan Konstanta FALSE TRUE TRUE TRUE 1589.061 3458.067
ARIMA(1,1,1)_Tanpa Konstanta FALSE TRUE TRUE TRUE 1589.684 3473.006
ARIMA(1,1,1)_Dengan Konstanta FALSE TRUE TRUE TRUE 1590.634 3447.734
ARIMA(20,1,0)_Tanpa Konstanta FALSE TRUE TRUE TRUE 1608.692 3052.098
ARIMA(20,1,0)_Dengan Konstanta FALSE TRUE TRUE TRUE 1609.618 3029.104
ARIMA(20,1,1)_Tanpa Konstanta FALSE TRUE TRUE TRUE 1609.887 3033.173
ARIMA(20,1,1)_Dengan Konstanta FALSE TRUE TRUE TRUE 1610.949 3013.586

**Model terbaik terpilih: ARIMA(1,1,0)_Tanpa Konstanta** — lolos seluruh uji diagnostik (signifikansi, normalitas, homogenitas, independensi) dengan AIC = 1587.90 dan MSE = 3478.1898.

coefs_best <- coef(best_model)

if (length(coefs_best) > 0) {
  data.frame(
    Parameter = names(coefs_best),
    Estimasi = round(coefs_best, 6)
  ) %>%
    tabel_gaya(caption = sprintf("Tabel 16. Estimasi Parameter Model Terbaik: %s", best_key))
} else {
  cat("Model terbaik tidak memiliki parameter AR/MA/konstanta yang diestimasi.\n")
}
Tabel 16. Estimasi Parameter Model Terbaik: ARIMA(1,1,0)_Tanpa Konstanta
Parameter Estimasi
ar1 -0.185637

13. Forecasting (Data Test)

13.1 Hasil Forecast vs Aktual

fc_result <- forecast(best_model, h = N_TEST, level = 95)

fc_trans    <- as.numeric(fc_result$mean)
lower_trans <- as.numeric(fc_result$lower)
upper_trans <- as.numeric(fc_result$upper)

if (round(lam_used) != 1) {
  fc_orig    <- InvBoxCox(fc_trans,    lam_used)
  lower_orig <- InvBoxCox(lower_trans, lam_used)
  upper_orig <- InvBoxCox(upper_trans, lam_used)
} else {
  fc_orig    <- fc_trans
  lower_orig <- lower_trans
  upper_orig <- upper_trans
}

df_fc <- data.frame(
  Periode  = 1:N_TEST,
  Tanggal  = format(test_dates, "%d/%m/%Y"),
  Aktual   = test,
  Forecast = round(fc_orig, 1),
  Lower_95 = round(lower_orig, 1),
  Upper_95 = round(upper_orig, 1)
)
df_fc$Error   <- round(df_fc$Aktual - df_fc$Forecast, 1)
df_fc$APE_pct <- round(abs(df_fc$Error) / df_fc$Aktual * 100, 4)

df_fc %>%
  tabel_gaya(
    caption = sprintf("Tabel 17. Hasil Forecast vs Aktual - Model %s", best_key),
    col.names = c("Periode", "Tanggal", "Aktual", "Forecast", "Lower 95%", "Upper 95%", "Error", "APE (%)")
  )
Tabel 17. Hasil Forecast vs Aktual - Model ARIMA(1,1,0)_Tanpa Konstanta
Periode Tanggal Aktual Forecast Lower 95% Upper 95% Error APE (%)
1 23/03/2026 3108.0 3129.2 3012.8 3245.6 -21.2 0.6821
2 24/03/2026 3176.0 3126.4 2976.3 3276.5 49.6 1.5617
3 25/03/2026 3216.0 3126.9 2947.2 3306.6 89.1 2.7705
4 26/03/2026 3209.0 3126.8 2922.1 3331.5 82.2 2.5615
5 27/03/2026 3256.0 3126.8 2899.8 3353.9 129.2 3.9681
6 30/03/2026 3168.0 3126.8 2879.5 3374.2 41.2 1.3005
7 31/03/2026 3157.0 3126.8 2860.7 3393.0 30.2 0.9566
8 01/04/2026 3194.0 3126.8 2843.2 3410.5 67.2 2.1039
9 02/04/2026 3100.0 3126.8 2826.7 3427.0 -26.8 0.8645
10 03/04/2026 3100.0 3126.8 2811.0 3442.7 -26.8 0.8645
11 06/04/2026 3106.0 3126.8 2796.1 3457.6 -20.8 0.6697
12 07/04/2026 3132.0 3126.8 2781.9 3471.8 5.2 0.1660
13 08/04/2026 3184.0 3126.8 2768.2 3485.5 57.2 1.7965
14 09/04/2026 3178.0 3126.8 2755.0 3498.7 51.2 1.6111
15 10/04/2026 3128.0 3126.8 2742.2 3511.5 1.2 0.0384
16 13/04/2026 3136.0 3126.8 2729.9 3523.8 9.2 0.2934
17 14/04/2026 3142.0 3126.8 2717.9 3535.7 15.2 0.4838
18 15/04/2026 3152.0 3126.8 2706.3 3547.4 25.2 0.7995
19 16/04/2026 3152.0 3126.8 2695.0 3558.7 25.2 0.7995
20 17/04/2026 3189.0 3126.8 2684.0 3569.7 62.2 1.9505
21 20/04/2026 3161.0 3126.8 2673.2 3580.4 34.2 1.0819
22 21/04/2026 3141.0 3126.8 2662.7 3590.9 14.2 0.4521
23 22/04/2026 3057.0 3126.8 2652.5 3601.2 -69.8 2.2833
24 23/04/2026 3004.0 3126.8 2642.4 3611.3 -122.8 4.0879
25 24/04/2026 2988.5 3126.8 2632.6 3621.1 -138.3 4.6277
26 27/04/2026 3031.0 3126.8 2622.9 3630.8 -95.8 3.1607
27 28/04/2026 3019.0 3126.8 2613.4 3640.2 -107.8 3.5707
28 30/04/2026 2976.5 3126.8 2604.1 3649.5 -150.3 5.0496
29 01/05/2026 2986.0 3126.8 2595.0 3658.7 -140.8 4.7153
30 07/05/2026 2982.0 3126.8 2586.0 3667.7 -144.8 4.8558
31 08/05/2026 2987.5 3126.8 2577.2 3676.5 -139.3 4.6628
32 11/05/2026 2935.0 3126.8 2568.5 3685.2 -191.8 6.5349
33 12/05/2026 2936.5 3126.8 2559.9 3693.8 -190.3 6.4805
34 13/05/2026 2960.5 3126.8 2551.5 3702.2 -166.3 5.6173
35 14/05/2026 2921.0 3126.8 2543.2 3710.5 -205.8 7.0455
36 15/05/2026 2919.5 3126.8 2535.0 3718.7 -207.3 7.1005

13.2 Perhitungan Metrik Akurasi

actual        <- test
forecast_vals <- fc_orig

MAPE  <- mean(abs((actual - forecast_vals) / actual)) * 100
SMAPE <- mean(abs(actual - forecast_vals) / ((abs(actual) + abs(forecast_vals)) / 2)) * 100
MSE_test <- mean((actual - forecast_vals)^2)
RMSE_val <- sqrt(MSE_test)
MAE_val  <- mean(abs(actual - forecast_vals))

kat <- if (MAPE < 10) {
  "Sangat Baik (MAPE < 10%)"
} else if (MAPE < 20) {
  "Baik (MAPE 10-20%)"
} else if (MAPE < 50) {
  "Cukup (MAPE 20-50%)"
} else {
  "Kurang Baik (MAPE > 50%)"
}

13.3 Grafik Hasil Forecasting

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

plot(train_dates, train, type = "l", col = warna_utama, lwd = 1.2,
     xlim = range(dates), ylim = range(c(train, test, lower_orig, upper_orig)),
     main = sprintf("Grafik Forecasting Saham Nippon Television\nModel Terbaik: %s", best_key),
     xlab = "Tanggal", ylab = "Price (JPY)")
lines(test_dates, test, col = "green3", lwd = 1.5)
lines(test_dates, fc_orig, col = warna_tersier, lty = 2, lwd = 1.5)
polygon(c(test_dates, rev(test_dates)), c(lower_orig, rev(upper_orig)),
        col = adjustcolor(warna_tersier, alpha.f = 0.1), border = NA)
abline(v = as.numeric(max(train_dates)), col = "gray50", lty = 3, lwd = 1)
legend("topleft",
       legend = c(sprintf("Training (%d data)", N_TRAIN), sprintf("Aktual Testing (%d data)", N_TEST),
                  "Forecast", "CI 95%"),
       col    = c(warna_utama, "green3", warna_tersier, adjustcolor(warna_tersier, 0.3)),
       lty    = c(1, 1, 2, NA), lwd = c(1.2, 1.5, 1.5, NA),
       pch    = c(NA, NA, NA, 15), pt.cex = c(NA, NA, NA, 2), bty = "n")
grid(col = "gray90")

plot(test_dates, test, type = "b", col = "green3", lwd = 2, pch = 16, cex = 0.8,
     ylim = range(c(test, lower_orig, upper_orig)),
     main = sprintf("Zoom Area Testing (%d data) | MAPE = %.2f%%  |  SMAPE = %.2f%%", N_TEST, MAPE, SMAPE),
     xlab = "Tanggal", ylab = "Price (JPY)")
lines(test_dates, fc_orig, col = warna_tersier, lty = 2, lwd = 2)
points(test_dates, fc_orig, col = warna_tersier, pch = 15, cex = 0.8)
polygon(c(test_dates, rev(test_dates)), c(lower_orig, rev(upper_orig)),
        col = adjustcolor(warna_tersier, alpha.f = 0.15), border = NA)
legend("topleft", legend = c("Aktual", "Forecast", "CI 95%"),
       col = c("green3", warna_tersier, adjustcolor(warna_tersier, 0.3)),
       lty = c(1, 2, NA), pch = c(16, 15, 15), pt.cex = c(0.8, 0.8, 2), bty = "n")
grid(col = "gray90")

par(mfrow = c(1, 1))

14. Evaluasi Akurasi Peramalan

data.frame(
  Metrik = c("MAPE", "SMAPE", "MSE", "RMSE", "MAE"),
  Nilai = c(
    sprintf("%.4f%%", MAPE), sprintf("%.4f%%", SMAPE),
    sprintf("%.4f", MSE_test), sprintf("%.4f", RMSE_val), sprintf("%.4f", MAE_val)
  )
) %>%
  tabel_gaya(caption = sprintf("Tabel 18. Nilai Akurasi Peramalan (Data Test, %d observasi)", N_TEST))
Tabel 18. Nilai Akurasi Peramalan (Data Test, 36 observasi)
Metrik Nilai
MAPE 2.7103%
SMAPE 2.6666%
MSE 10688.9743
RMSE 103.3875
MAE 82.1043

Berdasarkan nilai MAPE sebesar 2.7103%, tingkat akurasi model tergolong kategori Sangat Baik (MAPE < 10%). Nilai SMAPE (2.6666%) yang sejalan dengan MAPE mengindikasikan konsistensi pengukuran galat, sedangkan RMSE (103.3875) dan MAE (82.1043) menggambarkan besaran kesalahan peramalan dalam satuan asli harga saham (JPY).


15. Kesimpulan Akhir

data.frame(
  Keterangan = c(
    "Total Data", "Data Training (80%)", "Data Testing (20%)",
    "Model Terbaik", "Konstanta", "AIC", "BIC",
    "MAPE (Data Test)", "SMAPE (Data Test)", "MSE (Data Test)", "Kategori Akurasi"
  ),
  Nilai = c(
    length(price), N_TRAIN, N_TEST,
    sprintf("ARIMA(%d,%d,%d)", p_best, d_best, q_best),
    ifelse(ada_konstanta, "Ada", "Tidak Ada"),
    round(best_model$aic, 4), round(best_model$bic, 4),
    sprintf("%.4f%%", MAPE), sprintf("%.4f%%", SMAPE), sprintf("%.4f", MSE_test), kat
  )
) %>%
  tabel_gaya(caption = "Tabel 19. Ringkasan Kesimpulan Akhir Analisis")
Tabel 19. Ringkasan Kesimpulan Akhir Analisis
Keterangan Nilai
Total Data 181
Data Training (80%) 145
Data Testing (20%) 36
Model Terbaik ARIMA(1,1,0)
Konstanta Tidak Ada
AIC 1587.8964
BIC 1593.836
MAPE (Data Test) 2.7103%
SMAPE (Data Test) 2.6666%
MSE (Data Test) 10688.9743
Kategori Akurasi Sangat Baik (MAPE < 10%)

Berdasarkan seluruh tahapan analisis yang telah dilakukan, dapat disimpulkan bahwa:

  1. Dari 181 observasi harga saham Nippon Television Holdings Inc., data dibagi menjadi 145 data training (80%) dan 36 data testing (20%).
  2. Hasil uji Box-Cox menunjukkan status stasioneritas varian: Sudah stasioner dalam varian (tidak perlu transformasi).
  3. Hasil uji ADF menentukan orde differencing yang digunakan adalah d = 1.
  4. Berdasarkan proses identifikasi ACF/PACF, pembentukan kandidat, estimasi, uji signifikansi, dan verifikasi asumsi residual, model terbaik yang terpilih adalah ARIMA(1,1,0) dengan konstanta tidak ada, AIC = 1587.8964, dan BIC = 1593.836.
  5. Hasil peramalan pada data testing menghasilkan MAPE = 2.7103% dan SMAPE = 2.6666%, yang termasuk dalam kategori akurasi “Sangat Baik (MAPE < 10%)”.

Secara keseluruhan, model ARIMA yang diperoleh melalui pendekatan Box-Jenkins mampu menangkap pola pergerakan harga saham Nippon Television Holdings Inc. dengan tingkat akurasi yang telah dievaluasi secara menyeluruh melalui uji asumsi klasik ARIMA (signifikansi parameter, normalitas, homogenitas, dan independensi residual).