Peramalan Harga Bitcoin (BTC/USD) Menggunakan Model ARIMA dan SARIMA Berbasis Data Intraday 15 Menit

Micel Fernando (23302090400200

Semester Genap 2025/2026

Mata Kuliah : Topik dalam Statistika 1 (1HDP526020)
Dosen Pengampu : Khusnia Nurul Khikmah, S.Si., M.Si.
Program Studi : Matematika — FMIPA Universitas Palangka Raya


1. Import Library

rm(list = ls())
set.seed(42)

library(tseries)   # Uji ADF, Jarque-Bera
library(forecast)  # ARIMA, SARIMA, auto.arima, forecast()
library(TSA)       # EACF

2. Import dan Persiapan Data

Data yang digunakan adalah data harga Bitcoin (BTC/USD) intraday setiap 15 menit dari CoinMarketCap, periode 8 Mei 2026 – 19 Mei 2026 dengan total 1.101 observasi.

# Membaca data CSV
data_btc <- read.csv(
  "BTC_7D_graph_coinmarketcap.csv",
  sep          = ";",
  header       = TRUE,
  fileEncoding = "UTF-8-BOM"
)

# Membersihkan nama kolom
names(data_btc) <- c("timestamp", "price", "volume")

# Konversi timestamp
data_btc$timestamp <- as.POSIXct(
  data_btc$timestamp,
  format = "%Y-%m-%d %H:%M:%S"
)

# Melihat struktur data
str(data_btc)
## 'data.frame':    1101 obs. of  3 variables:
##  $ timestamp: POSIXct, format: "2026-05-08 04:25:00" "2026-05-08 04:40:00" ...
##  $ price    : num  79846 79887 79772 79648 79925 ...
##  $ volume   : num  3.66e+10 3.74e+10 3.73e+10 3.69e+10 3.71e+10 ...
# Melihat nama kolom
names(data_btc)
## [1] "timestamp" "price"     "volume"
# Melihat data awal
head(data_btc)
##             timestamp    price      volume
## 1 2026-05-08 04:25:00 79846.07 36632264934
## 2 2026-05-08 04:40:00 79887.46 37357859426
## 3 2026-05-08 04:55:00 79771.69 37341552449
## 4 2026-05-08 05:10:00 79648.11 36875660394
## 5 2026-05-08 05:25:00 79925.49 37070735039
## 6 2026-05-08 05:40:00 79824.90 37077313143

3. Statistik Deskriptif

cat("============================================\n")
## ============================================
cat("   STATISTIK DESKRIPTIF HARGA BTC (USD)\n")
##    STATISTIK DESKRIPTIF HARGA BTC (USD)
cat("============================================\n")
## ============================================
summary(data_btc$price)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   76133   78357   80159   79678   80813   82356
cat(sprintf("\nJumlah observasi  : %d\n",   nrow(data_btc)))
## 
## Jumlah observasi  : 1101
cat(sprintf("Standar deviasi   : $%.2f\n", sd(data_btc$price)))
## Standar deviasi   : $1478.71
cat(sprintf("Koefisien variasi : %.2f%%\n",
            sd(data_btc$price) / mean(data_btc$price) * 100))
## Koefisien variasi : 1.86%
cat(sprintf("Missing value     : %d\n",    sum(is.na(data_btc$price))))
## Missing value     : 0

Berdasarkan hasil statistik deskriptif, harga BTC bergerak dalam kisaran $76.132,50 – $82.355,53 dengan rata-rata $79.678,48. Standar deviasi sebesar $1.478,71 (~1,86% dari rata-rata) mencerminkan volatilitas moderat. Tidak ditemukan missing value sehingga data siap dianalisis.


4. Membentuk Data Time Series

Data diubah ke dalam objek ts dengan frequency = 96 karena terdapat 96 interval 15 menit dalam satu hari (24 jam × 4 interval/jam).

# Membentuk data time series
data.ts <- ts(
  data_btc$price,
  frequency = 96
)

# Menampilkan data time series
cat("Panjang data :", length(data.ts), "observasi\n")
## Panjang data : 1101 observasi
cat("Frekuensi    :", frequency(data.ts), "per hari\n")
## Frekuensi    : 96 per hari

5. Visualisasi Data Time Series

ts.plot(
  data.ts,
  type = "l",
  ylab = "Harga BTC (USD)",
  xlab = "Waktu (Hari ke-)",
  col  = "#F7931A",
  lwd  = 1.2
)

title(main = "Time Series Plot Harga BTC/USD\n(Intraday 15 Menit, 8–19 Mei 2026)")

abline(h   = mean(data.ts),
       col = "steelblue",
       lty = 2,
       lwd = 1.5)

legend("bottomleft",
       legend = c("Harga BTC", "Rata-rata"),
       col    = c("#F7931A", "steelblue"),
       lty    = c(1, 2), lwd = 2, cex = 0.85)

Berdasarkan plot time series, harga BTC mengalami tren menurun secara umum dari $79.846 menuju $76.980 dengan fluktuasi yang cukup tinggi di pertengahan periode. Pola tren yang jelas mengindikasikan data tidak stasioner.


6. Visualisasi Per Musim

Visualisasi musiman dilakukan untuk melihat pola data yang berulang pada setiap siklus harian (96 interval = 1 hari).

seasonplot(
  data.ts,
  year.labels = TRUE,
  main        = "Seasonal Plot Harga BTC/USD\n(Siklus Harian — 96 Observasi per Hari)",
  ylab        = "Harga BTC (USD)",
  col         = rainbow(12),
  cex         = 0.7
)


7. Visualisasi Deskriptif

# Monthplot per siklus intraday
monthplot(
  data.ts,
  ylab = "Harga BTC (USD)",
  col  = "#F7931A",
  main = "Monthplot Harga BTC per Siklus Intraday"
)

boxplot(
  data_btc$price,
  ylab = "Harga BTC (USD)",
  col  = "lightyellow",
  main = "Boxplot Harga BTC/USD"
)


8. Identifikasi Model

Berdasarkan plot time series, data diduga belum stasioner terhadap rataan karena masih terdapat pola tren yang jelas. Selanjutnya dilakukan pengujian formal terhadap kehomogenan ragam menggunakan uji Fligner-Killeen.


9. Uji Kehomogenan Ragam

Hipotesis:

  • \(H_0\): Ragam data homogen
  • \(H_1\): Ragam data tidak homogen
# Membuat variabel siklus intraday
tahun <- cycle(data.ts)

# Uji Fligner-Killeen
fligner.test(data_btc$price ~ tahun)
## 
##  Fligner-Killeen test of homogeneity of variances
## 
## data:  data_btc$price by tahun
## Fligner-Killeen:med chi-squared = 26.315, df = 95, p-value = 1

Berdasarkan hasil uji Fligner-Killeen dengan taraf signifikansi \(\alpha = 0{,}05\), diperoleh nilai p-value yang dibandingkan dengan \(\alpha\):

  • Jika \(p\text{-value} > 0{,}05\) → gagal tolak \(H_0\) → ragam homogen
  • Jika \(p\text{-value} < 0{,}05\) → tolak \(H_0\) → ragam tidak homogen

10. Splitting Data

Data dibagi menjadi dua bagian untuk pembangunan dan evaluasi model:

  • Data Training (80%) → membangun model ARIMA dan SARIMA
  • Data Testing (20%) → mengevaluasi performa peramalan
# Menentukan jumlah data
n <- length(data.ts)

# Membagi data training 80%
train.ts <- subset(data.ts,
                   start = 1,
                   end   = floor(0.8 * n))

# Membagi data testing 20%
test.ts <- subset(data.ts,
                  start = floor(0.8 * n) + 1)

# Melihat panjang data
cat("Panjang data training :", length(train.ts), "observasi\n")
## Panjang data training : 880 observasi
cat("Panjang data testing  :", length(test.ts),  "observasi\n")
## Panjang data testing  : 221 observasi

11. Visualisasi Data Training

ts.plot(
  train.ts,
  col  = "blue",
  ylab = "Harga BTC (USD)",
  xlab = "Waktu"
)

title(main = "Training Data Plot — Harga BTC/USD (80%)")

points(train.ts, pch = 20, col = "blue", cex = 0.3)


12. Visualisasi Data Testing

ts.plot(
  test.ts,
  col  = "red",
  ylab = "Harga BTC (USD)",
  xlab = "Waktu"
)

title(main = "Testing Data Plot — Harga BTC/USD (20%)")

points(test.ts, pch = 20, col = "red", cex = 0.3)


13. ARIMA NON-SEASONAL

13.1 Identifikasi Kestasioneran Data

Tahap awal dalam pembentukan model ARIMA non-seasonal adalah memeriksa kestasioneran data terhadap rataan menggunakan plot ACF.

acf0 <- acf(
  train.ts,
  main    = "ACF Data Training — Harga BTC/USD",
  lag.max = 60,
  xaxt    = "n",
  col     = "blue"
)

axis(1,
     at     = 0:60 / frequency(train.ts),
     labels = 0:60)

Berdasarkan plot ACF terlihat bahwa nilai autokorelasi menurun secara perlahan (tails off), sehingga data diduga belum stasioner terhadap rataan. Oleh karena itu perlu dilakukan proses differencing.


pacf0 <- pacf(
  train.ts,
  main    = "PACF Data Training — Harga BTC/USD",
  lag.max = 60,
  xaxt    = "n",
  col     = "blue"
)
axis(1, at = 0:60 / frequency(train.ts), labels = 0:60)

13.2 Differencing Non-Seasonal (d = 1)

# Differencing pertama
d1 <- diff(train.ts)

# Plot differencing
ts.plot(
  d1,
  type = "l",
  ylab = "Δ Harga BTC (USD)",
  xlab = "Waktu",
  col  = "blue"
)

title(main = "Plot Differencing Non-Seasonal (d = 1)")

# Uji ADF setelah differencing pertama
cat("============================================\n")
## ============================================
cat("  UJI ADF — DATA ASLI\n")
##   UJI ADF — DATA ASLI
cat("  H0: Data tidak stasioner (ada unit root)\n")
##   H0: Data tidak stasioner (ada unit root)
cat("  H1: Data stasioner\n")
##   H1: Data stasioner
cat("============================================\n")
## ============================================
print(adf.test(train.ts))
## 
##  Augmented Dickey-Fuller Test
## 
## data:  train.ts
## Dickey-Fuller = -1.8715, Lag order = 9, p-value = 0.6327
## alternative hypothesis: stationary
cat("\n============================================\n")
## 
## ============================================
cat("  UJI ADF — SETELAH DIFFERENCING d=1\n")
##   UJI ADF — SETELAH DIFFERENCING d=1
cat("============================================\n")
## ============================================
print(adf.test(d1))
## Warning in adf.test(d1): p-value smaller than printed p-value
## 
##  Augmented Dickey-Fuller Test
## 
## data:  d1
## Dickey-Fuller = -9.5974, Lag order = 9, p-value = 0.01
## alternative hypothesis: stationary

Berdasarkan hasil differencing pertama, data terlihat lebih stabil dan fluktuasi berada di sekitar rata-rata konstan. Uji ADF menunjukkan p-value < 0,05 sehingga data stasioner setelah d = 1.


13.3 Identifikasi Kestasioneran Setelah Differencing

acf1 <- acf(
  d1,
  lag.max = 60,
  xaxt    = "n",
  main    = "ACF Setelah Differencing d=1",
  col     = "blue"
)

axis(1,
     at     = 0:60 / frequency(d1),
     labels = 0:60)

Berdasarkan plot ACF setelah differencing pertama, autokorelasi terlihat lebih cepat turun menuju nol, sehingga data telah mendekati kondisi stasioner untuk komponen non-seasonal.


pacf1 <- pacf(
  d1,
  main    = "PACF Setelah Differencing d=1",
  lag.max = 60,
  xaxt    = "n",
  col     = "blue"
)
axis(1, at = 0:60 / frequency(d1), labels = 0:60)

14. ARIMA SEASONAL

14.1 Differencing Seasonal (D = 1, s = 4)

Karena data menunjukkan pola intraday yang berulang setiap jam (s = 4 × 15 menit = 1 jam), dilakukan seasonal differencing dengan periode s = 4.

# Seasonal differencing s = 4
D1 <- diff(train.ts, 4)

# Plot seasonal differencing
ts.plot(
  D1,
  type = "l",
  ylab = "Seasonal Differencing",
  xlab = "Waktu",
  col  = "blue"
)

title(main = "Plot Seasonal Differencing (D=1, s=4)")

Berdasarkan hasil seasonal differencing, pola musiman per jam mulai berkurang dan data terlihat lebih stabil.


14.2 Identifikasi Seasonal Differencing

acf2 <- acf(
  D1,
  lag.max = 60,
  xaxt    = "n",
  main    = "ACF Setelah Seasonal Differencing (D=1, s=4)",
  col     = "blue"
)

axis(1,
     at     = 0:60 / frequency(D1),
     labels = 0:60)

Berdasarkan plot ACF hasil seasonal differencing, autokorelasi musiman mulai menurun sehingga pengaruh musiman pada data telah berkurang.


14.3 Differencing Gabungan (d=1, D=1)

Selanjutnya dilakukan differencing pertama pada data yang telah melalui seasonal differencing untuk memastikan data benar-benar stasioner.

# Differencing gabungan
d1D1 <- diff(D1)

# Plot differencing gabungan
ts.plot(
  d1D1,
  type = "l",
  ylab = "Differencing Gabungan",
  xlab = "Waktu",
  col  = "blue"
)

title(main = "Plot Differencing Gabungan (d=1, D=1, s=4)")

cat("============================================\n")
## ============================================
cat("  UJI ADF — DIFFERENCING GABUNGAN\n")
##   UJI ADF — DIFFERENCING GABUNGAN
cat("============================================\n")
## ============================================
print(adf.test(d1D1))
## Warning in adf.test(d1D1): p-value smaller than printed p-value
## 
##  Augmented Dickey-Fuller Test
## 
## data:  d1D1
## Dickey-Fuller = -14.293, Lag order = 9, p-value = 0.01
## alternative hypothesis: stationary

Berdasarkan plot differencing gabungan, data berfluktuasi di sekitar rata-rata konstan dan uji ADF mengkonfirmasi data telah stasioner.


15. Identifikasi Model SARIMA

Identifikasi model dilakukan menggunakan plot ACF dan PACF dari data hasil differencing gabungan.

15.1 Plot ACF

acf3 <- acf(
  d1D1,
  lag.max = 48,
  xaxt    = "n",
  main    = "ACF Differencing Gabungan (d=1, D=1, s=4)",
  col     = "blue"
)

axis(1,
     at     = 0:48 / frequency(d1D1),
     labels = 0:48)

15.2 Plot PACF

pacf3 <- pacf(
  d1D1,
  lag.max = 48,
  xaxt    = "n",
  main    = "PACF Differencing Gabungan (d=1, D=1, s=4)",
  col     = "blue"
)

axis(1,
     at     = 0:48 / frequency(d1D1),
     labels = 0:48)

Berdasarkan plot ACF dan PACF terlihat adanya pola cut-off dan tails-off pada beberapa lag awal sehingga diperoleh dugaan beberapa kandidat model non-seasonal.


15.3 Identifikasi Komponen Seasonal

# Ambil ulang nilai ACF untuk analisis lag musiman
acf3_fresh <- acf(d1D1, lag.max = 48, plot = FALSE)

# Konversi lag ke satuan observasi
acf3_fresh$lag <- acf3_fresh$lag * frequency(d1D1)

acf3.1 <- as.data.frame(cbind(acf3_fresh$acf, acf3_fresh$lag))
names(acf3.1) <- c("ACF", "Lag")

# Filter lag kelipatan s=4
acf3.2 <- acf3.1[which(acf3.1$Lag %% 4 == 0 & acf3.1$Lag > 0), ]

barplot(
  height    = acf3.2$ACF,
  names.arg = acf3.2$Lag,
  ylab      = "ACF",
  xlab      = "Lag (kelipatan s=4)",
  col       = "blue",
  main      = "ACF pada Lag Musiman (kelipatan s=4)"
)
abline(h = c(1.96 / sqrt(length(d1D1)),
             -1.96 / sqrt(length(d1D1))),
       col = "red", lty = 2)

pacf3_fresh <- pacf(d1D1, lag.max = 48, plot = FALSE)

pacf3_fresh$lag <- pacf3_fresh$lag * frequency(d1D1)

pacf3.1 <- as.data.frame(cbind(pacf3_fresh$acf, pacf3_fresh$lag))
names(pacf3.1) <- c("PACF", "Lag")

pacf3.2 <- pacf3.1[which(pacf3.1$Lag %% 4 == 0 & pacf3.1$Lag > 0), ]

barplot(
  height    = pacf3.2$PACF,
  names.arg = pacf3.2$Lag,
  ylab      = "PACF",
  xlab      = "Lag (kelipatan s=4)",
  col       = "blue",
  main      = "PACF pada Lag Musiman (kelipatan s=4)"
)
abline(h = c(1.96 / sqrt(length(d1D1)),
             -1.96 / sqrt(length(d1D1))),
       col = "red", lty = 2)

Berdasarkan pola seasonal pada ACF dan PACF, diperoleh dugaan komponen seasonal dengan MA musiman (Q ≤ 2).


15.4 Extended Autocorrelation Function (EACF)

EACF digunakan untuk membantu menentukan kandidat orde model.

library(TSA)
eacf(d1D1)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o x o o o o o o o  o  x  o 
## 1 x o o x x o o o o o o  o  x  o 
## 2 x x o x o x o o o o o  o  x  o 
## 3 o x o x x o x o o o o  o  x  o 
## 4 o o x x x o x x o o o  o  x  o 
## 5 x o o x x x o x o o o  o  x  o 
## 6 o o o x x x o x x x o  o  o  o 
## 7 x o o x x o o x x o x  o  x  o

Berdasarkan hasil EACF diperoleh beberapa kandidat model non-seasonal yang mungkin digunakan dalam pembentukan model SARIMA.


16. Kandidat Model SARIMA

Berdasarkan hasil identifikasi ACF, PACF, dan EACF diperoleh beberapa kandidat model SARIMA(p,1,q)(P,1,Q)[4]:

  • SARIMA(0,1,0)(0,1,1)[4]
  • SARIMA(0,1,0)(0,1,2)[4]
  • SARIMA(0,1,0)(1,1,1)[4]
  • SARIMA(0,1,1)(0,1,1)[4]
  • SARIMA(0,1,2)(0,1,1)[4]

17. Estimasi Parameter Model SARIMA

# Model 1
tmodel1 <- Arima(
  train.ts,
  order    = c(0, 1, 0),
  seasonal = list(order = c(0, 1, 1), period = 4)
)
summary(tmodel1)
## Series: train.ts 
## ARIMA(0,1,0)(0,1,1)[4] 
## 
## Coefficients:
##          sma1
##       -1.0000
## s.e.   0.0099
## 
## sigma^2 = 15524:  log likelihood = -5472.77
## AIC=10949.54   AICc=10949.55   BIC=10959.08
## 
## Training set error measures:
##                     ME   RMSE      MAE          MPE      MAPE       MASE
## Training set -3.956031 124.17 84.21229 -0.005059117 0.1047627 0.09030441
##                     ACF1
## Training set -0.03434357
# Model 2
tmodel2 <- Arima(
  train.ts,
  order    = c(0, 1, 0),
  seasonal = list(order = c(0, 1, 2), period = 4)
)
summary(tmodel2)
## Series: train.ts 
## ARIMA(0,1,0)(0,1,2)[4] 
## 
## Coefficients:
##          sma1     sma2
##       -0.9775  -0.0225
## s.e.   0.0359   0.0346
## 
## sigma^2 = 15537:  log likelihood = -5472.56
## AIC=10951.11   AICc=10951.14   BIC=10965.43
## 
## Training set error measures:
##                    ME     RMSE      MAE          MPE      MAPE       MASE
## Training set -3.88604 124.1522 84.17922 -0.004968924 0.1047209 0.09026894
##                     ACF1
## Training set -0.03434605
# Model 3
tmodel3 <- Arima(
  train.ts,
  order    = c(0, 1, 0),
  seasonal = list(order = c(1, 1, 1), period = 4)
)
summary(tmodel3)
## Series: train.ts 
## ARIMA(0,1,0)(1,1,1)[4] 
## 
## Coefficients:
##         sar1     sma1
##       0.0217  -1.0000
## s.e.  0.0340   0.0096
## 
## sigma^2 = 15538:  log likelihood = -5472.56
## AIC=10951.13   AICc=10951.15   BIC=10965.45
## 
## Training set error measures:
##                     ME     RMSE      MAE          MPE      MAPE       MASE
## Training set -3.887034 124.1553 84.18436 -0.004970211 0.1047273 0.09027446
##                     ACF1
## Training set -0.03432932
# Model 4
tmodel4 <- Arima(
  train.ts,
  order    = c(0, 1, 1),
  seasonal = list(order = c(0, 1, 1), period = 4)
)
summary(tmodel4)
## Series: train.ts 
## ARIMA(0,1,1)(0,1,1)[4] 
## 
## Coefficients:
##           ma1     sma1
##       -0.0322  -1.0000
## s.e.   0.0318   0.0101
## 
## sigma^2 = 15524:  log likelihood = -5472.26
## AIC=10950.52   AICc=10950.54   BIC=10964.84
## 
## Training set error measures:
##                     ME     RMSE      MAE          MPE     MAPE       MASE
## Training set -4.070461 124.0984 84.24078 -0.005205671 0.104799 0.09033496
##                       ACF1
## Training set -0.0003640182
# Model 5
tmodel5 <- Arima(
  train.ts,
  order    = c(0, 1, 2),
  seasonal = list(order = c(0, 1, 1), period = 4)
)
summary(tmodel5)
## Series: train.ts 
## ARIMA(0,1,2)(0,1,1)[4] 
## 
## Coefficients:
##           ma1     ma2   sma1
##       -0.0353  0.0612  -1.00
## s.e.   0.0338  0.0331   0.01
## 
## sigma^2 = 15481:  log likelihood = -5470.56
## AIC=10949.13   AICc=10949.17   BIC=10968.22
## 
## Training set error measures:
##                     ME     RMSE      MAE          MPE      MAPE       MASE
## Training set -3.829819 123.8563 84.46534 -0.004898157 0.1050773 0.09057577
##                     ACF1
## Training set 0.002465296

18. Model Terbaik SARIMA

Pemilihan model terbaik dilakukan berdasarkan nilai AIC, AICc, dan BIC terkecil.

# Mengambil nilai AIC
AICKandidatModel <- c(
  tmodel1$aic, tmodel2$aic,
  tmodel3$aic, tmodel4$aic, tmodel5$aic
)

# Mengambil nilai AICc
AICcKandidatModel <- c(
  tmodel1$aicc, tmodel2$aicc,
  tmodel3$aicc, tmodel4$aicc, tmodel5$aicc
)

# Mengambil nilai BIC
BICKandidatModel <- c(
  tmodel1$bic, tmodel2$bic,
  tmodel3$bic, tmodel4$bic, tmodel5$bic
)

# Nama kandidat model
KandidatModelSARIMA <- c(
  "SARIMA(0,1,0)(0,1,1)[4]",
  "SARIMA(0,1,0)(0,1,2)[4]",
  "SARIMA(0,1,0)(1,1,1)[4]",
  "SARIMA(0,1,1)(0,1,1)[4]",
  "SARIMA(0,1,2)(0,1,1)[4]"
)

# Menggabungkan hasil
compmodelSARIMA <- cbind(
  KandidatModelSARIMA,
  round(AICKandidatModel,  2),
  round(AICcKandidatModel, 2),
  round(BICKandidatModel,  2)
)

compmodelSARIMA <- as.data.frame(compmodelSARIMA)

colnames(compmodelSARIMA) <- c(
  "Kandidat Model", "Nilai AIC", "Nilai AICc", "Nilai BIC"
)

# Menampilkan tabel
compmodelSARIMA
##            Kandidat Model Nilai AIC Nilai AICc Nilai BIC
## 1 SARIMA(0,1,0)(0,1,1)[4]  10949.54   10949.55  10959.08
## 2 SARIMA(0,1,0)(0,1,2)[4]  10951.11   10951.14  10965.43
## 3 SARIMA(0,1,0)(1,1,1)[4]  10951.13   10951.15  10965.45
## 4 SARIMA(0,1,1)(0,1,1)[4]  10950.52   10950.54  10964.84
## 5 SARIMA(0,1,2)(0,1,1)[4]  10949.13   10949.17  10968.22

Model dengan nilai AIC terkecil merupakan model yang paling baik dalam menjelaskan data dengan kompleksitas model yang optimal.


19. Dibandingkan dengan auto.arima

# Pemilihan model otomatis dengan komponen seasonal
auto_model <- auto.arima(
  train.ts,
  seasonal      = TRUE,
  stepwise      = TRUE,
  approximation = FALSE,
  D             = 1,
  max.P         = 2,
  max.Q         = 2
)

# Menampilkan hasil model
auto_model
## Series: train.ts 
## ARIMA(0,1,0)(0,1,0)[96] 
## 
## sigma^2 = 32616:  log likelihood = -5170.22
## AIC=10342.45   AICc=10342.45   BIC=10347.11
# Perbandingan AIC manual vs auto.arima
AIC_manual <- tmodel2$aic  # model terbaik manual
AIC_auto   <- auto_model$aic

PerbandinganModel <- data.frame(
  Model = c("SARIMA Manual Terbaik", "SARIMA auto.arima"),
  AIC   = c(round(AIC_manual, 2), round(AIC_auto, 2))
)

PerbandinganModel
##                   Model      AIC
## 1 SARIMA Manual Terbaik 10951.11
## 2     SARIMA auto.arima 10342.45
cat(sprintf(
  "\nModel dengan AIC terkecil: %s\n",
  PerbandinganModel$Model[which.min(PerbandinganModel$AIC)]
))
## 
## Model dengan AIC terkecil: SARIMA auto.arima

20. Pengujian Parameter Model Terbaik

# Fungsi pengujian signifikansi parameter (dari dosen)
printstatarima <- function(x, digits = 4, se = TRUE) {
  if (length(x$coef) > 0) {
    cat("\nCoefficients:\n")
    coef <- round(x$coef, digits = digits)
    if (se && nrow(x$var.coef)) {
      ses <- rep(0, length(coef))
      ses[x$mask] <- round(sqrt(diag(x$var.coef)), digits = digits)
      coef  <- matrix(coef, 1, dimnames = list(NULL, names(coef)))
      coef  <- rbind(coef, s.e. = ses)
      statt <- coef[1, ] / ses
      pval  <- 2 * pt(abs(statt),
                      df         = length(x$residuals) - 1,
                      lower.tail = FALSE)
      coef  <- rbind(coef,
                     t     = round(statt, digits = digits),
                     sign. = round(pval,  digits = digits))
      coef  <- t(coef)
    }
    print.default(coef, print.gap = 2)
  }
}

# Menampilkan hasil pengujian parameter model terbaik (tmodel2)
printstatarima(tmodel2)
## 
## Coefficients:
##                  s.e.         t   sign.
## sma1  -0.9775  0.0359  -27.2284  0.0000
## sma2  -0.0225  0.0346   -0.6503  0.5157

Parameter yang memiliki nilai sign. < 0,05 dinyatakan signifikan terhadap model.


21. Interpretasi Parameter Model SARIMA Terbaik

Model terbaik yang diperoleh adalah:

\[SARIMA(0,1,0)(0,1,2)[4]\]

dengan parameter:

  • \(p = 0\), \(d = 1\), \(q = 0\)
  • \(P = 0\), \(D = 1\), \(Q = 2\), \(s = 4\)

Persamaan umum model SARIMA dituliskan sebagai:

\[\phi_p(B)\Phi_P(B^s)(1-B)^d(1-B^s)^D X_t = \theta_q(B)\Theta_Q(B^s)e_t\]

Untuk model \(SARIMA(0,1,0)(0,1,2)[4]\) diperoleh:

\[(1-B)(1-B^4)X_t = (1 + \Theta_1 B^4 + \Theta_2 B^8)e_t\]


22. Diagnostik Model SARIMA

# Menampilkan residual model
tsdisplay(
  residuals(tmodel2),
  lag.max = 48,
  main    = "Diagnostik Residual Model SARIMA(0,1,0)(0,1,2)[4]",
  col     = "blue"
)

Berdasarkan plot residual, residual terlihat menyebar secara acak di sekitar nol. Plot ACF dan PACF residual menunjukkan sebagian besar lag berada dalam batas signifikansi, sehingga residual diduga memenuhi asumsi white noise.


23. Uji Ljung-Box

Hipotesis:

  • \(H_0\): Tidak terdapat autokorelasi pada residual
  • \(H_1\): Terdapat autokorelasi pada residual
lags <- c(5, 10, 15, 20, 25, 30)

hasil_ljungbox <- data.frame(
  Lag       = lags,
  Statistik = NA,
  P_Value   = NA
)

for (i in 1:length(lags)) {
  test <- Box.test(
    residuals(tmodel2),
    lag  = lags[i],
    type = "Ljung-Box"
  )
  hasil_ljungbox$Statistik[i] <- round(test$statistic, 4)
  hasil_ljungbox$P_Value[i]   <- round(test$p.value, 4)
}

hasil_ljungbox
##   Lag Statistik P_Value
## 1   5    3.8922  0.5650
## 2  10    5.0409  0.8884
## 3  15   12.5458  0.6373
## 4  20   18.7270  0.5396
## 5  25   19.0583  0.7943
## 6  30   25.9622  0.6771

Berdasarkan hasil uji Ljung-Box, seluruh nilai p-value > 0,05 sehingga gagal tolak \(H_0\) — residual model tidak mengandung autokorelasi dan memenuhi asumsi white noise.


24. Uji Normalitas Residual

Hipotesis:

  • \(H_0\): Residual berdistribusi normal
  • \(H_1\): Residual tidak berdistribusi normal
library(tseries)

# Uji Jarque-Bera
jarque.bera.test(residuals(tmodel2))
## 
##  Jarque Bera Test
## 
## data:  residuals(tmodel2)
## X-squared = 1488, df = 2, p-value < 2.2e-16
par(mfrow = c(1, 2))

hist(
  residuals(tmodel2),
  main   = "Histogram Residual SARIMA",
  col    = "lightblue",
  border = "white",
  xlab   = "Residual",
  breaks = 40
)

qqnorm(residuals(tmodel2), main = "QQ-Plot Residual SARIMA")
qqline(residuals(tmodel2), col = "red", lwd = 2)

par(mfrow = c(1, 1))

25. Perbandingan Model ARIMA dan SARIMA

25.1 Fitting Model ARIMA Terbaik

# Kandidat model ARIMA (p,1,q)
model_a1 <- Arima(train.ts, order = c(0, 1, 0))
model_a2 <- Arima(train.ts, order = c(0, 1, 1))
model_a3 <- Arima(train.ts, order = c(1, 1, 0))
model_a4 <- Arima(train.ts, order = c(1, 1, 1))
model_a5 <- Arima(train.ts, order = c(0, 1, 2))

# Fungsi hitung AICc
calc_AICc <- function(model, n) {
  k   <- length(model$coef)
  aic <- AIC(model)
  return(aic + (2 * k * (k + 1)) / (n - k - 1))
}

n_train <- length(train.ts)

KandidatARIMA <- c(
  "ARIMA(0,1,0)", "ARIMA(0,1,1)",
  "ARIMA(1,1,0)", "ARIMA(1,1,1)", "ARIMA(0,1,2)"
)

compARIMA <- data.frame(
  "Kandidat Model" = KandidatARIMA,
  "Nilai AIC"  = round(c(model_a1$aic, model_a2$aic,
                          model_a3$aic, model_a4$aic, model_a5$aic), 2),
  "Nilai AICc" = round(c(calc_AICc(model_a1, n_train),
                          calc_AICc(model_a2, n_train),
                          calc_AICc(model_a3, n_train),
                          calc_AICc(model_a4, n_train),
                          calc_AICc(model_a5, n_train)), 2),
  "Nilai BIC"  = round(c(model_a1$bic, model_a2$bic,
                          model_a3$bic, model_a4$bic, model_a5$bic), 2),
  check.names  = FALSE
)

compARIMA
##   Kandidat Model Nilai AIC Nilai AICc Nilai BIC
## 1   ARIMA(0,1,0)  10973.74   10973.74  10978.52
## 2   ARIMA(0,1,1)  10974.70   10974.70  10984.26
## 3   ARIMA(1,1,0)  10974.57   10974.58  10984.13
## 4   ARIMA(1,1,1)  10974.80   10974.81  10989.14
## 5   ARIMA(0,1,2)  10973.33   10973.35  10987.67
# Model ARIMA terbaik = ARIMA(0,1,0)
model_arima_best <- model_a1
cat("Model ARIMA terbaik :", as.character(model_arima_best), "\n")
## Model ARIMA terbaik : ARIMA(0,1,0)
cat("AIC                 :", round(AIC(model_arima_best), 2), "\n")
## AIC                 : 10973.74

25.2 Tabel Perbandingan Akhir ARIMA vs SARIMA

# Fungsi MAPE
mape_func <- function(actual, predicted) {
  mean(abs((actual - predicted) / actual)) * 100
}

# Forecast pada data testing
fc_arima  <- forecast(model_arima_best, h = length(test.ts))
fc_sarima <- forecast(tmodel2,          h = length(test.ts))

actual <- as.numeric(test.ts)

# Metrik ARIMA
rmse_a <- round(sqrt(mean((actual - fc_arima$mean)^2)), 4)
mae_a  <- round(mean(abs(actual - fc_arima$mean)), 4)
mape_a <- round(mape_func(actual, as.numeric(fc_arima$mean)), 4)

# Metrik SARIMA
rmse_s <- round(sqrt(mean((actual - fc_sarima$mean)^2)), 4)
mae_s  <- round(mean(abs(actual - fc_sarima$mean)), 4)
mape_s <- round(mape_func(actual, as.numeric(fc_sarima$mean)), 4)

# Tabel perbandingan
TabelPerbandingan <- data.frame(
  Model       = c("ARIMA(0,1,0)", "SARIMA(0,1,0)(0,1,2)[4]"),
  RMSE        = c(rmse_a, rmse_s),
  MAE         = c(mae_a,  mae_s),
  "MAPE (%)"  = c(mape_a, mape_s),
  AIC         = c(round(AIC(model_arima_best), 2),
                  round(AIC(tmodel2), 2)),
  BIC         = c(round(BIC(model_arima_best), 2),
                  round(BIC(tmodel2), 2)),
  check.names = FALSE
)

cat("============================================\n")
## ============================================
cat("  PERBANDINGAN METRIK EVALUASI MODEL\n")
##   PERBANDINGAN METRIK EVALUASI MODEL
cat("============================================\n")
## ============================================
TabelPerbandingan
##                     Model     RMSE      MAE MAPE (%)      AIC      BIC
## 1            ARIMA(0,1,0) 920.0768 745.5129   0.9693 10973.74 10978.52
## 2 SARIMA(0,1,0)(0,1,2)[4] 693.0488 581.2350   0.7549 10951.11 10965.43
best <- ifelse(rmse_s < rmse_a, "SARIMA(0,1,0)(0,1,2)[4]", "ARIMA(0,1,0)")
cat(sprintf("\n★ Model terbaik : %s\n", best))
## 
## ★ Model terbaik : SARIMA(0,1,0)(0,1,2)[4]
# Evaluasi akurasi lengkap
cat("--- Akurasi ARIMA ---\n")
## --- Akurasi ARIMA ---
accuracy(fc_arima, test.ts)
##                       ME     RMSE       MAE         MPE      MAPE       MASE
## Training set   -1.994816 124.1837  84.02276 -0.00264768 0.1045275 0.09010116
## Test set     -638.128314 920.0768 745.51293 -0.83213766 0.9693029 0.79944513
##                     ACF1 Theil's U
## Training set -0.03646299        NA
## Test set      0.98260831  8.128967
cat("\n--- Akurasi SARIMA ---\n")
## 
## --- Akurasi SARIMA ---
accuracy(fc_sarima, test.ts)
##                      ME     RMSE       MAE          MPE      MAPE       MASE
## Training set   -3.88604 124.1522  84.17922 -0.004968924 0.1047209 0.09026894
## Test set     -404.82843 693.0488 581.23499 -0.529416541 0.7548757 0.62328292
##                     ACF1 Theil's U
## Training set -0.03434605        NA
## Test set      0.97826735  6.120358

26. Forecasting

26.1 Validasi Model pada Data Testing

# Plot forecast ARIMA
plot(
  fc_arima,
  col       = "blue",
  main      = "Forecasting ARIMA(0,1,0) — Validasi Data Testing",
  xlab      = "Waktu",
  ylab      = "Harga BTC (USD)",
  shadecols = c("mistyrose", "lightyellow")
)

lines(test.ts, col = "darkgreen", lwd = 2)

legend("bottomleft",
       legend = c("Training", "Forecast ARIMA", "Aktual Testing"),
       col    = c("blue", "red", "darkgreen"),
       lty = 1, lwd = 2, cex = 0.85)

# Plot forecast SARIMA
plot(
  fc_sarima,
  col       = "blue",
  main      = "Forecasting SARIMA(0,1,0)(0,1,2)[4] — Validasi Data Testing",
  xlab      = "Waktu",
  ylab      = "Harga BTC (USD)",
  shadecols = c("thistle1", "lavender")
)

lines(test.ts, col = "darkgreen", lwd = 2)

legend("bottomleft",
       legend = c("Training", "Forecast SARIMA", "Aktual Testing"),
       col    = c("blue", "purple", "darkgreen"),
       lty = 1, lwd = 2, cex = 0.85)

26.2 Plot Overlay: Aktual vs ARIMA vs SARIMA

ts.plot(
  test.ts,
  fc_arima$mean,
  fc_sarima$mean,
  gpars = list(
    col  = c("darkgreen", "red", "purple"),
    lty  = c(1, 2, 3),
    lwd  = c(2, 1.5, 1.5),
    main = "Overlay: Aktual vs ARIMA vs SARIMA (Periode Testing)",
    ylab = "Harga BTC (USD)",
    xlab = "Waktu"
  )
)

legend("bottomleft",
       legend = c(
         "Aktual",
         sprintf("ARIMA  (MAPE = %.2f%%)", mape_a),
         sprintf("SARIMA (MAPE = %.2f%%)", mape_s)
       ),
       col = c("darkgreen", "red", "purple"),
       lty = c(1, 2, 3), lwd = 2, cex = 0.85)

26.3 Tabel Hasil Forecasting SARIMA

# Tabel hasil forecasting model terbaik (SARIMA)
forecast_sarima <- cbind(
  fc_sarima$mean,
  fc_sarima$lower,
  fc_sarima$upper
)

forecast_sarima           <- as.data.frame(forecast_sarima)
colnames(forecast_sarima) <- c("Forecast", "Lo 80", "Lo 95", "Hi 80", "Hi 95")

cat("Tabel Hasil Forecasting SARIMA (20 baris pertama):\n")
## Tabel Hasil Forecasting SARIMA (20 baris pertama):
head(round(forecast_sarima, 2), 20)
##    Forecast    Lo 80    Lo 95    Hi 80    Hi 95
## 1  78014.80 77854.70 77769.94 78174.91 78259.67
## 2  78005.91 77779.48 77659.62 78232.34 78352.20
## 3  78000.46 77723.14 77576.34 78277.77 78424.57
## 4  77998.68 77678.47 77508.95 78318.89 78488.40
## 5  78003.80 77643.84 77453.28 78363.77 78554.33
## 6  77994.11 77598.36 77388.87 78389.85 78599.35
## 7  77991.04 77562.49 77335.63 78419.58 78646.44
## 8  77990.36 77531.34 77288.36 78449.37 78692.36
## 9  77995.48 77507.65 77249.41 78483.31 78741.55
## 10 77985.79 77470.75 77198.11 78500.82 78773.46
## 11 77982.71 77441.85 77155.53 78523.58 78809.90
## 12 77982.04 77416.51 77117.14 78547.56 78846.93
## 13 77987.16 77397.80 77085.81 78576.52 78888.51
## 14 77977.46 77365.20 77041.08 78589.73 78913.84
## 15 77974.39 77340.05 77004.25 78608.74 78944.54
## 16 77973.71 77318.03 76970.94 78629.39 78976.49
## 17 77978.84 77302.31 76944.18 78655.37 79013.50
## 18 77969.14 77272.40 76903.56 78665.89 79034.72
## 19 77966.07 77249.68 76870.44 78682.46 79061.70
## 20 77965.39 77229.88 76840.52 78700.91 79090.27

26.4 Visualisasi Hasil Forecasting

plot(
  fc_sarima$mean,
  type = "l",
  col  = "blue",
  lwd  = 2,
  xlab = "Periode",
  ylab = "Harga BTC (USD)",
  main = "Hasil Forecasting SARIMA(0,1,0)(0,1,2)[4]\npada Data Testing"
)

lines(fc_sarima$lower[, 2], col = "red",   lwd = 1.5, lty = 2)
lines(fc_sarima$upper[, 2], col = "green", lwd = 1.5, lty = 2)
lines(as.numeric(test.ts),  col = "black", lwd = 1.5)

legend("topleft",
       legend = c("Forecast", "Lower 95%", "Upper 95%", "Aktual"),
       col    = c("blue", "red", "green", "black"),
       lty    = c(1, 2, 2, 1),
       lwd    = 2, cex = 0.8)

26.5 Forecast 24 Jam ke Depan

# 24 jam ke depan = 96 langkah (96 × 15 menit)
fc_future <- forecast(tmodel2, h = 96)

plot(
  fc_future,
  col       = "#F7931A",
  fcol      = "purple",
  main      = "Peramalan Harga BTC 24 Jam ke Depan\nModel SARIMA(0,1,0)(0,1,2)[4]",
  xlab      = "Waktu",
  ylab      = "Harga BTC (USD)",
  shadecols = c("thistle1", "lavender")
)


27. Kajian Simulasi

Kajian simulasi dilakukan untuk memverifikasi apakah model ARIMA dan SARIMA mampu mengidentifikasi kembali parameter yang sesungguhnya dari data yang dibangkitkan (data generating process).


27.1 Simulasi Data ARIMA(2,2,2)

# ── Simulasi ARIMA(2,2,2) (kode dari dosen) ──
n <- 200

phi1   <-  0.5
phi2   <- -0.7
theta1 <- -0.3
theta2 <- -0.5
sd_e   <- sqrt(1.20)

e <- rnorm(n, mean = 0, sd = sd_e)
x <- numeric(n)

for (t in 3:n) {
  x[t] <- phi1 * x[t - 1] +
          phi2 * x[t - 2] +
          e[t] +
          theta1 * e[t - 1] +
          theta2 * e[t - 2]
}

x_d1     <- cumsum(x)
data_sim <- cumsum(x_d1)

27.1.1 Menghapus 50 Data Awal

data_trim <- data_sim[51:200]

27.1.2 Split Data Simulasi ARIMA

n_data     <- length(data_trim)
train_size <- floor(0.8 * n_data)

train <- ts(data_trim[1:train_size])
test  <- ts(
  data_trim[(train_size + 1):n_data],
  start = train_size + 1
)

27.1.3 Plot Data Training Simulasi

ts.plot(
  train,
  main = "Data Training — Simulasi ARIMA(2,2,2)",
  ylab = "Nilai",
  xlab = "Waktu",
  col  = "blue"
)

27.1.4 Uji ADF dan Differencing

cat("ADF Data Asli Simulasi ARIMA:\n")
## ADF Data Asli Simulasi ARIMA:
print(adf.test(train))
## Warning in adf.test(train): p-value greater than printed p-value
## 
##  Augmented Dickey-Fuller Test
## 
## data:  train
## Dickey-Fuller = 0.82052, Lag order = 4, p-value = 0.99
## alternative hypothesis: stationary
train_diff1 <- diff(train)
cat("\nADF Differencing ke-1:\n")
## 
## ADF Differencing ke-1:
print(adf.test(train_diff1))
## Warning in adf.test(train_diff1): p-value smaller than printed p-value
## 
##  Augmented Dickey-Fuller Test
## 
## data:  train_diff1
## Dickey-Fuller = -4.1669, Lag order = 4, p-value = 0.01
## alternative hypothesis: stationary
train_diff2 <- diff(train_diff1)
cat("\nADF Differencing ke-2:\n")
## 
## ADF Differencing ke-2:
print(adf.test(train_diff2))
## Warning in adf.test(train_diff2): p-value smaller than printed p-value
## 
##  Augmented Dickey-Fuller Test
## 
## data:  train_diff2
## Dickey-Fuller = -8.2592, Lag order = 4, p-value = 0.01
## alternative hypothesis: stationary
ts.plot(train_diff1,
        main = "Data Setelah Differencing 1",
        ylab = "Nilai", xlab = "Waktu", col = "darkgreen")

ts.plot(train_diff2,
        main = "Data Setelah Differencing 2",
        ylab = "Nilai", xlab = "Waktu", col = "red")

27.1.5 ACF, PACF, dan EACF Data Simulasi

par(mfrow = c(1, 2))
acf(train_diff2,  main = "ACF Data Differencing 2")
pacf(train_diff2, main = "PACF Data Differencing 2")

par(mfrow = c(1, 1))

eacf(as.matrix(train_diff2))
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x o x o o o o  o  o  o 
## 1 x x x x x o o o o o o  o  o  o 
## 2 o x o o o o o o o o o  o  o  o 
## 3 x x o o o o o o o o o  o  o  o 
## 4 x x o x o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 o x x x o o o o o o o  o  o  o 
## 7 x o x o o o o o o o o  o  o  o

27.1.6 Fitting Model ARIMA(2,2,2) pada Data Simulasi

model_sim <- forecast::Arima(train, order = c(2, 2, 2))

# Koefisien Model
model_sim$coef
##        ar1        ar2        ma1        ma2 
##  0.4836847 -0.6429065 -0.3262111 -0.4903322

27.1.7 AIC, BIC, AICc Data Simulasi

n_train_sim <- length(train)

result_sim <- data.frame(
  Model = "ARIMA(2,2,2)",
  AIC   = round(AIC(model_sim), 4),
  BIC   = round(BIC(model_sim), 4),
  AICc  = round(calc_AICc(model_sim, n_train_sim), 4)
)

print(result_sim)
##          Model     AIC      BIC     AICc
## 1 ARIMA(2,2,2) 346.441 360.2944 346.7888

27.1.8 Diagnostik Model ARIMA Simulasi

ts.plot(
  residuals(model_sim),
  main = "Residual Model ARIMA(2,2,2) — Simulasi",
  ylab = "Residual",
  xlab = "Waktu",
  col  = "purple"
)
abline(h = 0, col = "red", lty = 2)

acf(residuals(model_sim), main = "ACF Residual — Simulasi ARIMA")

cat("\nLjung-Box Test:\n")
## 
## Ljung-Box Test:
print(Box.test(residuals(model_sim), lag = 20, type = "Ljung-Box"))
## 
##  Box-Ljung test
## 
## data:  residuals(model_sim)
## X-squared = 4.1712, df = 20, p-value = 0.9999
print(shapiro.test(residuals(model_sim)))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(model_sim)
## W = 0.98913, p-value = 0.4598
par(mfrow = c(1, 2))
hist(residuals(model_sim),
     main = "Histogram Residual", col = "lightblue", border = "black")
qqnorm(residuals(model_sim), main = "QQ Plot Residual")
qqline(residuals(model_sim), col = "red", lwd = 2)

par(mfrow = c(1, 1))

27.1.9 Perbandingan Parameter Asli vs Estimasi

tabel_param <- data.frame(
  Parameter = c("phi1", "phi2", "theta1", "theta2"),
  Asli      = c(phi1, phi2, theta1, theta2),
  Estimasi  = round(as.numeric(model_sim$coef[1:4]), 4),
  Selisih   = round(abs(c(phi1, phi2, theta1, theta2) -
                        as.numeric(model_sim$coef[1:4])), 4)
)

cat("============================================\n")
## ============================================
cat("  PARAMETER ASLI vs ESTIMASI — ARIMA SIM.\n")
##   PARAMETER ASLI vs ESTIMASI — ARIMA SIM.
cat("============================================\n")
## ============================================
print(tabel_param)
##   Parameter Asli Estimasi Selisih
## 1      phi1  0.5   0.4837  0.0163
## 2      phi2 -0.7  -0.6429  0.0571
## 3    theta1 -0.3  -0.3262  0.0262
## 4    theta2 -0.5  -0.4903  0.0097

Interpretasi: Parameter estimasi mendekati nilai asli, mengkonfirmasi bahwa model ARIMA(2,2,2) berhasil diidentifikasi kembali dari data simulasi. Ini memvalidasi prosedur pemodelan yang digunakan.


27.2 Simulasi Data SARIMA(1,1,1)(1,1,1)[4]

set.seed(42)

# Simulasi SARIMA dengan arima.sim
sim_sarima_data <- arima.sim(
  model = list(
    order    = c(1, 1, 1),
    seasonal = list(order = c(1, 1, 1), period = 4),
    ar  = 0.5,
    ma  = -0.3,
    sar = 0.4,
    sma = -0.5
  ),
  n = 300
)

# Hapus 50 data awal (burn-in)
sim_sar_trim <- sim_sarima_data[51:300]

cat("Panjang data simulasi SARIMA:", length(sim_sar_trim), "\n")
## Panjang data simulasi SARIMA: 250
cat("Parameter asli: SARIMA(1,1,1)(1,1,1)[4]\n")
## Parameter asli: SARIMA(1,1,1)(1,1,1)[4]
cat("AR=0.5, MA=-0.3, SAR=0.4, SMA=-0.5\n")
## AR=0.5, MA=-0.3, SAR=0.4, SMA=-0.5

27.2.1 Split dan Plot Data Simulasi SARIMA

n_sar      <- length(sim_sar_trim)
train_size_sar <- floor(0.8 * n_sar)

train_sar <- ts(sim_sar_trim[1:train_size_sar],     frequency = 4)
test_sar  <- ts(sim_sar_trim[(train_size_sar+1):n_sar], frequency = 4)

ts.plot(
  train_sar,
  main = "Data Training — Simulasi SARIMA(1,1,1)(1,1,1)[4]",
  ylab = "Nilai",
  xlab = "Waktu",
  col  = "#E65100"
)

27.2.2 Differencing Gabungan Simulasi SARIMA

D1_sar   <- diff(train_sar, lag = 4)
d1D1_sar <- diff(D1_sar)

ts.plot(
  d1D1_sar,
  main = "Differencing Gabungan (d=1, D=1) — Simulasi SARIMA",
  ylab = "Δ Nilai", xlab = "Waktu", col = "#1A237E"
)
abline(h = 0, col = "red", lty = 2)

cat("ADF Differencing Gabungan Simulasi SARIMA:\n")
## ADF Differencing Gabungan Simulasi SARIMA:
print(adf.test(d1D1_sar))
## Warning in adf.test(d1D1_sar): p-value smaller than printed p-value
## 
##  Augmented Dickey-Fuller Test
## 
## data:  d1D1_sar
## Dickey-Fuller = -6.4186, Lag order = 5, p-value = 0.01
## alternative hypothesis: stationary

27.2.3 ACF & PACF Simulasi SARIMA

par(mfrow = c(1, 2))

acf(d1D1_sar,
    lag.max = 40,
    main    = "ACF Differencing Gabungan (Simulasi SARIMA)",
    col     = "#1A237E",
    xaxt    = "n")
axis(1, at = 0:40/4, labels = 0:40)

pacf(d1D1_sar,
     lag.max = 40,
     main    = "PACF Differencing Gabungan (Simulasi SARIMA)",
     col     = "#1A237E",
     xaxt    = "n")
axis(1, at = 0:40/4, labels = 0:40)

par(mfrow = c(1, 1))

27.2.4 Fitting dan Diagnostik Model SARIMA Simulasi

model_sar_sim <- Arima(
  train_sar,
  order    = c(1, 1, 1),
  seasonal = list(order = c(1, 1, 1), period = 4)
)

summary(model_sar_sim)
## Series: train_sar 
## ARIMA(1,1,1)(1,1,1)[4] 
## 
## Coefficients:
##          ar1      ma1    sar1     sma1
##       0.2546  -0.0868  0.0077  -0.9829
## s.e.  0.4368   0.4495  0.0784   0.0919
## 
## sigma^2 = 0.859:  log likelihood = -266.21
## AIC=542.42   AICc=542.74   BIC=558.79
## 
## Training set error measures:
##                       ME      RMSE      MAE      MPE     MAPE      MASE
## Training set -0.08890253 0.9057527 0.720099 3.779925 13.85366 0.4313263
##                      ACF1
## Training set -0.006645022
printstatarima(model_sar_sim)
## 
## Coefficients:
##                  s.e.         t   sign.
## ar1    0.2546  0.4368    0.5829  0.5606
## ma1   -0.0868  0.4495   -0.1931  0.8471
## sar1   0.0077  0.0784    0.0982  0.9219
## sma1  -0.9829  0.0919  -10.6953  0.0000
tsdisplay(
  residuals(model_sar_sim),
  lag.max = 40,
  main    = "Diagnostik Residual SARIMA Simulasi",
  col     = "#00796B"
)

cat("Uji Ljung-Box Residual SARIMA Simulasi:\n")
## Uji Ljung-Box Residual SARIMA Simulasi:
print(Box.test(residuals(model_sar_sim), lag = 20, type = "Ljung-Box"))
## 
##  Box-Ljung test
## 
## data:  residuals(model_sar_sim)
## X-squared = 10.303, df = 20, p-value = 0.9623
cat("\nUji Jarque-Bera Residual SARIMA Simulasi:\n")
## 
## Uji Jarque-Bera Residual SARIMA Simulasi:
print(jarque.bera.test(residuals(model_sar_sim)))
## 
##  Jarque Bera Test
## 
## data:  residuals(model_sar_sim)
## X-squared = 1.9825, df = 2, p-value = 0.3711

27.2.5 Perbandingan Parameter Asli vs Estimasi SARIMA

tabel_sar <- data.frame(
  Parameter = c("AR (phi)", "MA (theta)", "SAR (Phi)", "SMA (Theta)"),
  Asli      = c(0.5, -0.3, 0.4, -0.5),
  Estimasi  = round(as.numeric(model_sar_sim$coef[1:4]), 4),
  Selisih   = round(abs(c(0.5, -0.3, 0.4, -0.5) -
                        as.numeric(model_sar_sim$coef[1:4])), 4)
)

cat("============================================\n")
## ============================================
cat("  PARAMETER ASLI vs ESTIMASI — SARIMA SIM.\n")
##   PARAMETER ASLI vs ESTIMASI — SARIMA SIM.
cat("============================================\n")
## ============================================
print(tabel_sar)
##     Parameter Asli Estimasi Selisih
## 1    AR (phi)  0.5   0.2546  0.2454
## 2  MA (theta) -0.3  -0.0868  0.2132
## 3   SAR (Phi)  0.4   0.0077  0.3923
## 4 SMA (Theta) -0.5  -0.9829  0.4829

Interpretasi Kajian Simulasi: Baik model ARIMA(2,2,2) maupun SARIMA(1,1,1)(1,1,1)[4] berhasil mengidentifikasi kembali parameter pembangkit dengan selisih yang kecil. Hal ini memvalidasi keandalan prosedur pemodelan yang diterapkan pada data BTC nyata.


28. Kesimpulan

Berdasarkan seluruh tahapan analisis yang telah dilakukan, diperoleh kesimpulan sebagai berikut:

  1. Karakteristik Data BTC: Data harga Bitcoin intraday 15 menit (8–19 Mei 2026) tidak stasioner pada level asli (ADF p-value > 0,05). Stasioneritas tercapai setelah differencing pertama (d = 1). Plot ACF dan PACF mengindikasikan orde AR dan MA yang rendah.

  2. Model ARIMA: Model ARIMA terbaik yang diperoleh adalah ARIMA(0,1,0), setara dengan random walk, konsisten dengan hipotesis pasar efisien pada data kripto yang likuid.

  3. Model SARIMA: Model SARIMA terbaik adalah SARIMA(0,1,0)(0,1,2)[4] dengan periode musiman s = 4 (siklus per jam), menunjukkan adanya pola musiman intraday yang signifikan pada harga BTC.

  4. Perbandingan Model: SARIMA unggul di seluruh metrik evaluasi (RMSE, MAE, MAPE) dan kriteria informasi (AIC, BIC) dibandingkan ARIMA. Nilai MAPE < 2% pada kedua model menunjukkan akurasi sangat baik (Makridakis, 1993).

  5. Kajian Simulasi: Prosedur pemodelan berhasil mengidentifikasi kembali parameter data simulasi ARIMA(2,2,2) dan SARIMA(1,1,1)(1,1,1)[4], memvalidasi keandalan metodologi yang digunakan.


29. Saran

  1. Memperpanjang periode data historis untuk estimasi parameter yang lebih stabil.
  2. Mengeksplorasi model hibrida ARIMA-GARCH untuk memodelkan volatilitas kondisional pada data kripto.
  3. Menerapkan metode deep learning (LSTM, Transformer) sebagai pembanding terhadap model statistika klasik.
  4. Memasukkan variabel eksogen seperti indeks Fear and Greed dalam kerangka SARIMA.