library(readxl)
library(dplyr)
library(tidyr)
library(tibble)
library(lubridate)
library(stringr)
library(scales)
library(ggplot2)
library(gridExtra)
library(reshape2)
library(tseries)
library(urca)
library(forecast)
library(lmtest)
library(TSA)
library(xts)
library(zoo)
library(FinTS)
library(rugarch)
library(knitr)
library(kableExtra)
library(performance)# read data
kursUSD <- read_xlsx("D:/UNY/MySta/SEM 5/Ekonometrika/Time Series ARCH GARCH/Kurs_Transaksi_USD_2015-2025.xlsx", sheet = 1)
kursUSD## # A tibble: 2,623 Ă— 5
## NO Nilai `Kurs Jual` `Kurs Beli` Tanggal
## <dbl> <dbl> <dbl> <dbl> <chr>
## 1 1 1 16763. 16597. 9/30/2025 12:00:00 AM
## 2 2 1 16859. 16691. 9/29/2025 12:00:00 AM
## 3 3 1 16836. 16668. 9/26/2025 12:00:00 AM
## 4 4 1 16763. 16597. 9/25/2025 12:00:00 AM
## 5 5 1 16719. 16553. 9/24/2025 12:00:00 AM
## 6 6 1 16690. 16524. 9/23/2025 12:00:00 AM
## 7 7 1 16661. 16495. 9/22/2025 12:00:00 AM
## 8 8 1 16580. 16416. 9/19/2025 12:00:00 AM
## 9 9 1 16512. 16348. 9/18/2025 12:00:00 AM
## 10 10 1 16467. 16303. 9/17/2025 12:00:00 AM
## # ℹ 2,613 more rows
## tibble [2,623 Ă— 5] (S3: tbl_df/tbl/data.frame)
## $ NO : num [1:2623] 1 2 3 4 5 6 7 8 9 10 ...
## $ Nilai : num [1:2623] 1 1 1 1 1 1 1 1 1 1 ...
## $ Kurs Jual: num [1:2623] 16763 16859 16836 16763 16719 ...
## $ Kurs Beli: num [1:2623] 16597 16691 16668 16597 16553 ...
## $ Tanggal : chr [1:2623] "9/30/2025 12:00:00 AM" "9/29/2025 12:00:00 AM" "9/26/2025 12:00:00 AM" "9/25/2025 12:00:00 AM" ...
## Rows: 2,623
## Columns: 5
## $ NO <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ Nilai <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ `Kurs Jual` <dbl> 16763.40, 16858.88, 16835.76, 16763.40, 16719.18, 16690.03…
## $ `Kurs Beli` <dbl> 16596.60, 16691.13, 16668.24, 16596.60, 16552.82, 16523.97…
## $ Tanggal <chr> "9/30/2025 12:00:00 AM", "9/29/2025 12:00:00 AM", "9/26/20…
## # A tibble: 6 Ă— 5
## NO Nilai `Kurs Jual` `Kurs Beli` Tanggal
## <dbl> <dbl> <dbl> <dbl> <chr>
## 1 1 1 16763. 16597. 9/30/2025 12:00:00 AM
## 2 2 1 16859. 16691. 9/29/2025 12:00:00 AM
## 3 3 1 16836. 16668. 9/26/2025 12:00:00 AM
## 4 4 1 16763. 16597. 9/25/2025 12:00:00 AM
## 5 5 1 16719. 16553. 9/24/2025 12:00:00 AM
## 6 6 1 16690. 16524. 9/23/2025 12:00:00 AM
# Parsing tanggal & buat kolom Date
kursUSD <- kursUSD %>%
mutate(
Tanggal_raw = Tanggal,
# parse mm/dd/YYYY hh:mm:ss AM/PM
Tanggal_parsed = mdy_hms(Tanggal_raw, tz = "UTC"),
Date = as_date(Tanggal_parsed)
)
# Konversi kolom numeric yang akan dipakai
kursUSD <- kursUSD %>%
mutate(
Kurs_Jual = as.numeric(`Kurs Jual`),
Kurs_Beli = as.numeric(`Kurs Beli`)
)
# Sorting (urutan ascending berdasarkan Date)
kursUSD <- kursUSD %>%
arrange(Date)
# Cek duplikat tanggal
dups <- kursUSD %>% add_count(Date) %>% filter(n > 1)
if (nrow(dups) > 0) {
print(head(dups, 10))
} else {
print(0)
}## [1] 0
# Cek missing per kolom
missing_summary <- sapply(kursUSD, function(x) sum(is.na(x)))
missing_summary## NO Nilai Kurs Jual Kurs Beli Tanggal
## 0 0 0 0 0
## Tanggal_raw Tanggal_parsed Date Kurs_Jual Kurs_Beli
## 0 0 0 0 0
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 12506 13685 14393 14537 15256 17028
# Visualisasi Time Series
ggplot(kursUSD, aes(x = Date, y = Kurs_Jual)) +
geom_line(color = "darkblue", linewidth = 0.8) +
labs(
title = "Kurs Jual USD terhadap Rupiah (2015–2025)",
x = "Tahun",
y = "Nilai Kurs Jual (Rp)"
) +
scale_y_continuous(labels = comma) +
scale_x_date(
date_breaks = "1 year",
date_labels = "%Y",
limits = as.Date(c("2015-01-01", "2025-12-31"))
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1)
)# Distribusi Data
# Histogram + density plot
ggplot(kursUSD, aes(x = Kurs_Jual)) +
geom_histogram(bins = 40, fill = "skyblue", color = "white", alpha = 0.7) +
geom_density(color = "black", linewidth = 0.8) +
labs(title = "Distribusi Nilai Kurs Jual USD", x = "Kurs Jual (Rp)", y = "Frekuensi") +
theme_minimal()# Outlier Detection
# Boxplot
ggplot(kursUSD, aes(y = Kurs_Jual, x = "")) +
geom_boxplot(fill = "skyblue", width = 0.3, outlier.color = "red", outlier.size = 2) +
labs(title = "Boxplot Kurs Jual USD", y = "Nilai Kurs Jual (Rp)", x = "") +
theme_minimal()library(dplyr)
library(tseries)
df <- kursUSD %>% arrange(Date) %>% select(Date, Kurs_Jual)
# Train/Test split (time-ordered, 80% train, 20% test)
n_total <- nrow(df)
n_train <- floor(0.8 * n_total)
train_df <- df[1:n_train, ]
test_df <- df[(n_train + 1):n_total, ]
cat("Total data:", n_total, "\n")## Total data: 2623
## Jumlah Data Training: 2098
## Jumlah Data Testing: 525
# time series object untuk arima
train_ts <- ts(train_df$Kurs_Jual)
test_ts <- ts(test_df$Kurs_Jual)
# xts untuk GARCH (agar tanggal tidak hilang)
library(xts)
train_xts <- xts(train_df$Kurs_Jual, order.by = as.Date(train_df$Date))
test_xts <- xts(test_df$Kurs_Jual, order.by = as.Date(test_df$Date))library(dplyr)
library(ggplot2)
library(scales)
# bertipe Date
train_df <- train_df %>% mutate(Date = as.Date(Date))
test_df <- test_df %>% mutate(Date = as.Date(Date))
# combined data
combined <- bind_rows(
train_df %>% mutate(set = "Train"),
test_df %>% mutate(set = "Test")
)
# tanggal pemisah
separator_date <- min(test_df$Date)
# legend
p_combined <- ggplot(combined, aes(x = Date, y = Kurs_Jual, color = set)) +
geom_line(size = 0.8) +
# Pemisah_data_train
geom_vline(aes(xintercept = as.numeric(separator_date), color = "Pemisah_data_train"),
linetype = "dashed", size = 0.9, show.legend = TRUE) +
scale_color_manual(
name = "colour",
values = c(
"Train" = "darkblue",
"Test" = "darkred",
"Pemisah_data_train" = "darkgreen"
),
breaks = c("Train", "Test", "Pemisah_data_train")
) +
labs(
title = "Kurs Jual USD terhadap Rupiah (Training & Testing)",
x = "Tahun",
y = "Nilai Kurs Jual (Rp)"
) +
scale_y_continuous(labels = comma) +
scale_x_date(
date_breaks = "1 year",
date_labels = "%Y",
limits = as.Date(c("2015-01-01", "2025-12-31"))
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p_combinedInterpretasi: Plot ACF menunjukkan penurunan autokorelasi yang lambat (tailing off) dan tidak cepat menuju nol, yang mengindikasikan bahwa data belum stasioner pada level (kemungkinan mengandung tren).
Untuk memastikan secara statistik, dilakukan beberapa pengujian formal yaitu ADF Test, KPSS Test, dan Phillips-Perron Test.
##
## Augmented Dickey-Fuller Test
##
## data: train_ts
## Dickey-Fuller = -4.4762, Lag order = 12, p-value = 0.01
## alternative hypothesis: stationary
Interpretasi: Karena p-value < 0.05, maka dapat ditolak hipotesis nol (Hâ‚€) bahwa terdapat unit root. Ini menunjukkan indikasi awal bahwa data cenderung stasioner pada level, meskipun hasil ini perlu dikonfirmasi dengan uji lain (karena ADF kadang sensitif terhadap tren deterministik).
##
## KPSS Test for Level Stationarity
##
## data: train_ts
## KPSS Level = 14.928, Truncation lag parameter = 8, p-value = 0.01
nterpretasi: Pada KPSS, H₀ = data stasioner pada level. Karena p-value < 0.05, maka H₀ ditolak. Ini berarti data tidak stasioner pada level. ⇒ Hasil ini bertolak belakang dengan ADF, sehingga dilakukan uji lanjutan pada data setelah diferensiasi.
##
## Phillips-Perron Unit Root Test
##
## data: train_ts
## Dickey-Fuller Z(alpha) = -31.619, Truncation lag parameter = 8, p-value
## = 0.01
## alternative hypothesis: stationary
Interpretasi: PP test juga menunjukkan hasil signifikan (p < 0.05) yang mengindikasikan data stasioner. Namun karena KPSS menyatakan sebaliknya, maka dilakukan differencing untuk memastikan.
# Differencing Ordo 1
dif1 <- diff(train_ts, differences = 1)
plot.ts(dif1, xlab="Waktu", ylab="Kurs Jual USD Diff Ordo 1")##
## Augmented Dickey-Fuller Test
##
## data: dif1
## Dickey-Fuller = -12.034, Lag order = 12, p-value = 0.01
## alternative hypothesis: stationary
Interpretasi: Setelah dilakukan differencing ordo 1, data menjadi stasioner (p < 0.05).
##
## KPSS Test for Level Stationarity
##
## data: dif1
## KPSS Level = 0.031213, Truncation lag parameter = 8, p-value = 0.1
Interpretasi: Nilai p-value > 0.05 ⇒ gagal menolak H₀ ⇒ data stasioner setelah differencing.
##
## Phillips-Perron Unit Root Test
##
## data: dif1
## Dickey-Fuller Z(alpha) = -1953.7, Truncation lag parameter = 8, p-value
## = 0.01
## alternative hypothesis: stationary
##
## ###############################################
## # Augmented Dickey-Fuller Test Unit Root Test #
## ###############################################
##
## Test regression none
##
##
## Call:
## lm(formula = z.diff ~ z.lag.1 - 1 + z.diff.lag)
##
## Residuals:
## Min 1Q Median 3Q Max
## -387.70 -25.65 2.83 29.01 471.65
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## z.lag.1 -0.734117 0.061094 -12.016 <2e-16 ***
## z.diff.lag1 -0.125745 0.059243 -2.123 0.0339 *
## z.diff.lag2 -0.082092 0.057378 -1.431 0.1527
## z.diff.lag3 -0.055006 0.055319 -0.994 0.3202
## z.diff.lag4 -0.034647 0.053233 -0.651 0.5152
## z.diff.lag5 -0.003903 0.050509 -0.077 0.9384
## z.diff.lag6 -0.023671 0.047874 -0.494 0.6210
## z.diff.lag7 0.003576 0.044880 0.080 0.9365
## z.diff.lag8 -0.033375 0.041840 -0.798 0.4251
## z.diff.lag9 0.043644 0.038247 1.141 0.2540
## z.diff.lag10 0.032493 0.034065 0.954 0.3403
## z.diff.lag11 0.049125 0.028954 1.697 0.0899 .
## z.diff.lag12 0.006822 0.021949 0.311 0.7560
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 59.83 on 2071 degrees of freedom
## Multiple R-squared: 0.4356, Adjusted R-squared: 0.432
## F-statistic: 122.9 on 13 and 2071 DF, p-value: < 2.2e-16
##
##
## Value of test-statistic is: -12.0163
##
## Critical values for test statistics:
## 1pct 5pct 10pct
## tau1 -2.58 -1.95 -1.62
## [1] -2.575829
Interpretasi: Karena -12.0163 < -2.58, maka H₀ (unit root) ditolak. ⇒ Data stasioner setelah diferensiasi pertama.
## AR/MA
## 0 1 2 3 4 5 6 7 8 9 10
## 0 x x o o o o o o x o o
## 1 x o o o o o o o x o o
## 2 x x o o o o o o x o o
## 3 x o o o o o o o x o o
## 4 x o x x o o o o x o o
## 5 x x o x o o o o o o o
## 6 x x o x x x o o o o o
## 7 x x o o x x o o o o o
## 8 x x o x x x x x o o o
## 9 x x x x x x x x o o o
## 10 x o x x x o x x x o o
Interpretasi Plot: - Garis biru putus-putus menunjukkan batas signifikansi 95% (±1.96/sqrt(n)). - Batang yang melewati batas tersebut berarti signifikan secara statistik. - Pada plot ACF dan PACF, lag 1 terlihat signifikan sedangkan lag 2 dan 9 tidak menunjukkan pola sistematis (kemungkinan fluktuasi acak). - Plot ACF setelah diferensiasi pertama (diff 1) menunjukkan pola cut off setelah lag 1, sedangkan setelahnya nilai ACF cenderung menurun perlahan dan berada dalam batas signifikansi. → Ini mengindikasikan adanya komponen MA(1). - Plot PACF menunjukkan spike signifikan pada lag 1, kemudian cepat turun dan berfluktuasi kecil di sekitar nol. → Ini mengindikasikan adanya komponen AR(1). - Berdasarkan EACF, tanda “o” pertama (pola segitiga kosong) muncul sekitar posisi (p,q) = (1,1) atau (1,2).
Maka model tentatif yang dapat diuji meliputi: ARIMA(1,1,1) ARIMA(1,1,2) ARIMA(2,1,1)
library(forecast)
library(lmtest)
# Model kandidat berdasarkan hasil identifikasi ACF, PACF, dan EACF
# Model estimation
model_111 <- arima(train_ts, order = c(1,1,1), include.mean = TRUE, method = "ML")
model_112 <- arima(train_ts, order = c(1,1,2), include.mean = TRUE, method = "ML")
model_211 <- arima(train_ts, order = c(2,1,1), include.mean = TRUE, method = "ML")
# Display parameters
model_111##
## Call:
## arima(x = train_ts, order = c(1, 1, 1), include.mean = TRUE, method = "ML")
##
## Coefficients:
## ar1 ma1
## 0.5846 -0.4530
## s.e. 0.1186 0.1309
##
## sigma^2 estimated as 3598: log likelihood = -11560.65, aic = 23125.3
##
## Call:
## arima(x = train_ts, order = c(1, 1, 2), include.mean = TRUE, method = "ML")
##
## Coefficients:
## ar1 ma1 ma2
## 0.7360 -0.6001 -0.0380
## s.e. 0.1526 0.1555 0.0382
##
## sigma^2 estimated as 3596: log likelihood = -11560.1, aic = 23126.2
##
## Call:
## arima(x = train_ts, order = c(2, 1, 1), include.mean = TRUE, method = "ML")
##
## Coefficients:
## ar1 ar2 ma1
## 0.8233 -0.0502 -0.6872
## s.e. 0.1941 0.0455 0.1923
##
## sigma^2 estimated as 3595: log likelihood = -11560.04, aic = 23126.09
##
## z test of coefficients:
##
## Estimate Std. Error z value Pr(>|z|)
## ar1 0.58464 0.11862 4.9286 8.284e-07 ***
## ma1 -0.45305 0.13091 -3.4607 0.0005389 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## z test of coefficients:
##
## Estimate Std. Error z value Pr(>|z|)
## ar1 0.736000 0.152609 4.8228 1.416e-06 ***
## ma1 -0.600142 0.155453 -3.8606 0.0001131 ***
## ma2 -0.038029 0.038182 -0.9960 0.3192427
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## z test of coefficients:
##
## Estimate Std. Error z value Pr(>|z|)
## ar1 0.823254 0.194074 4.2420 2.216e-05 ***
## ar2 -0.050173 0.045451 -1.1039 0.269642
## ma1 -0.687170 0.192288 -3.5736 0.000352 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
model_comp <- data.frame(
Model = c("ARIMA(1,1,1)", "ARIMA(1,1,2)", "ARIMA(2,1,1)"),
AIC = c(AIC(model_111), AIC(model_112), AIC(model_211)),
BIC = c(BIC(model_111), BIC(model_112), BIC(model_211)),
logLik = c(logLik(model_111), logLik(model_112), logLik(model_211))
)
print(model_comp)## Model AIC BIC logLik
## 1 ARIMA(1,1,1) 23127.30 23144.25 -11560.65
## 2 ARIMA(1,1,2) 23128.20 23150.79 -11560.10
## 3 ARIMA(2,1,1) 23128.09 23150.68 -11560.04
Model terbaik model tentatif adalah ARIMA(1,1,1) karena:
##
## Fitting models using approximations to speed things up...
##
## ARIMA(2,1,2) with drift : Inf
## ARIMA(0,1,0) with drift : 23170.65
## ARIMA(1,1,0) with drift : 23124.65
## ARIMA(0,1,1) with drift : 23132.14
## ARIMA(0,1,0) : 23169.45
## ARIMA(2,1,0) with drift : 23120.91
## ARIMA(3,1,0) with drift : 23121.67
## ARIMA(2,1,1) with drift : 23119.76
## ARIMA(1,1,1) with drift : 23117.15
## ARIMA(1,1,2) with drift : 23118.83
## ARIMA(0,1,2) with drift : 23125.54
## ARIMA(1,1,1) : 23115.52
## ARIMA(0,1,1) : 23130.78
## ARIMA(1,1,0) : 23123.17
## ARIMA(2,1,1) : 23118.08
## ARIMA(1,1,2) : 23117.16
## ARIMA(0,1,2) : 23124.11
## ARIMA(2,1,0) : 23119.34
## ARIMA(2,1,2) : Inf
##
## Now re-fitting the best model(s) without approximations...
##
## ARIMA(1,1,1) : 23127.32
##
## Best model: ARIMA(1,1,1)
## Series: train_ts
## ARIMA(1,1,1)
##
## Coefficients:
## ar1 ma1
## 0.5790 -0.4469
## s.e. 0.1196 0.1317
##
## sigma^2 = 3601: log likelihood = -11560.65
## AIC=23127.31 AICc=23127.32 BIC=23144.25
Berikut dilakukan pemeriksaan asumsi model secara eksploratif melalui Normal Q-Q Plot, Plot ACF, dan PACF.
model_111r <- arima(train_ts, order = c(1,1,1), include.mean = TRUE, method = "ML")
residual_111 <- model_111r$residuals
# Eksplorasi
par(mfrow=c(1,1))
qqnorm(residual_111)
qqline(residual_111, col = "blue", lwd = 2)
Interpretasi: - Berdasarkan hasil diatas, secara eksploratif Normal Q-Q
Plot menunjukkan bahwa sisaan tidak mengikuti sebaran normal karena
terdapat titik-titik yang tidak berada di sekitar garis. Penyimpangan
titik dari garis diagonal menunjukkan adanya heavy tail pada residual,
yang umum terjadi pada data keuangan dengan volatilitas tinggi. - plot
ACF dan PACF terlihat bahwa terdapat lag yang signifikan yakni pada lag
9 dan 32. Hal ini menunjukkan bahwa terdapat sedikit gejala autokorelasi
pada sisaan. Selanjutnya, untuk memastikan kembali akan dilakukan uji
asumsi.
Uji Autokorelasi (Ljung–Box Test)
##
## Box-Ljung test
##
## data: residual_111
## X-squared = 41.232, df = 30, p-value = 0.08312
Berdasarkan hasil uji Ljung-Box di atas, diperoleh p-value = 0.08312 > 0.05 sehingga gagal menolak H0. Artinya, tidak terdapat autokorelasi signifikan pada sisaan model ARIMA(1,1,1). Dengan demikian, asumsi white noise residual terpenuhi.
Uji Normalitas (Kolmogorov–Smirnov Test)
##
## Asymptotic one-sample Kolmogorov-Smirnov test
##
## data: scale(residual_111)
## D = 0.085351, p-value = 1.062e-13
## alternative hypothesis: two-sided
Berdasarkan hasil uji Kolmogorov-Smirnov di atas dapat disimpulkan bahwa H0 ditolak karena p-value < 0.05 sehingga sisaan dari model ARIMA (1,1,1) tidak mengikuti sebaran Normal.
Uji Nilai Tengah Sisaan
##
## One Sample t-test
##
## data: residual_111
## t = 0.68116, df = 2097, p-value = 0.4958
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## -1.675881 3.459641
## sample estimates:
## mean of x
## 0.8918802
Berdasarkan hasil uji nilai tengah sisaan di atas dapat disimpulkan bahwa H0 tidak ditolak karena p-value > 0.05 sehingga nilai tengah sisaan sama dengan nol → tidak ada bias sistematis.
Berdasarkan hasil uji eksploratif dan statistik, residual model ARIMA(1,1,1) tidak memiliki autokorelasi yang signifikan dan memiliki nilai tengah nol, namun tidak berdistribusi normal. Kondisi ini mengindikasikan kemungkinan adanya heteroskedastisitas (volatilitas berubah-ubah), sehingga perlu dilakukan uji ARCH/GARCH pada tahap berikutnya.
Jika p-value < 0.05 → ada indikasi efek ARCH.
##
## ARCH LM-test; Null hypothesis: no ARCH effects
##
## data: residual_111
## Chi-squared = 607.6, df = 30, p-value < 2.2e-16
p-value < 0.05 → tolak H0 → ada indikasi heteroskedastisitas → lanjut ke model GARCH.
uji ARCH menunjukkan p-value < 0.05, maka lanjut untuk menentukan ordo terbaik (p,q) pada model GARCH.
Membandingkan AIC/BIC dari beberapa ordo. misal (1,1), (1,2), (2,1), (2,2)
# PEMILIHAN MODEL GARCH BERDASARKAN AIC (p,q)
library(fGarch)
library(texreg)
# Model 1: ARCH(1)
arch1 <- garchFit(~ arma(1,1) + garch(1,0),
data = residual_111, trace = FALSE)
extract(arch1,
include.nobs = FALSE,
include.aic = TRUE,
include.loglik = FALSE)##
##
## coef. s.e. p
## mu 0.9772589 1.63539457 0.55012844
## ar1 -0.5293635 0.31893567 0.09695844
## ma1 0.5108796 0.31996521 0.11033876
## omega 2151.0515409 93.30622857 0.00000000
## alpha1 0.4123800 0.04586699 0.00000000
##
## GOF dec. places
## AIC 10.84818 TRUE
# Model 2: GARCH(1,1)
garch11 <- garchFit(~ arma(1,1) + garch(1,1),
data = residual_111, trace = FALSE)
extract(garch11,
include.nobs = FALSE,
include.aic = TRUE,
include.loglik = FALSE)##
##
## coef. s.e. p
## mu 0.2730082 0.30855366 3.762646e-01
## ar1 0.6216782 0.12403834 5.387167e-07
## ma1 -0.6925774 0.11402331 1.247635e-09
## omega 96.0088453 21.41688382 7.365000e-06
## alpha1 0.1211486 0.01786907 1.203460e-11
## beta1 0.8516833 0.02081923 0.000000e+00
##
## GOF dec. places
## AIC 10.67331 TRUE
# Model 3: GARCH(1,2)
garch12 <- garchFit(~ arma(1,1) + garch(1,2),
data = residual_111, trace = FALSE)
extract(garch12,
include.nobs = FALSE,
include.aic = TRUE,
include.loglik = FALSE)##
##
## coef. s.e. p
## mu 0.2889341 0.31545011 3.596972e-01
## ar1 0.6027082 0.11648167 2.287979e-07
## ma1 -0.6817271 0.10594034 1.234655e-10
## omega 120.3835461 26.78305261 6.964659e-06
## alpha1 0.1609991 0.02400998 2.007083e-11
## beta1 0.4209053 0.11054126 1.402787e-04
## beta2 0.3839581 0.10118561 1.478863e-04
##
## GOF dec. places
## AIC 10.66894 TRUE
# Model 4: GARCH(2,1)
garch21 <- garchFit(~ arma(1,1) + garch(2,1),
data = residual_111, trace = FALSE)
extract(garch21,
include.nobs = FALSE,
include.aic = TRUE,
include.loglik = FALSE)##
##
## coef. s.e. p
## mu 0.27420159 0.30991274 3.762805e-01
## ar1 0.62069868 0.12536242 7.374428e-07
## ma1 -0.69169003 0.11499034 1.796634e-09
## omega 96.37699299 29.70072207 1.174763e-03
## alpha1 0.12144479 0.02197681 3.275358e-08
## alpha2 0.00000001 0.03536109 9.999998e-01
## beta1 0.85125321 0.03299679 0.000000e+00
##
## GOF dec. places
## AIC 10.67413 TRUE
Interpretasi – Uji Ordo ARCH–GARCH Optimal
Berdasarkan hasil perbandingan nilai Akaike Information Criterion (AIC) dari beberapa ordo model ARCH–GARCH, diketahui bahwa model GARCH(1,2) memiliki nilai AIC paling kecil (10.66894). Dengan demikian, model GARCH(1,2) dengan mean model ARMA(1,1) dipilih sebagai model terbaik untuk memodelkan variansi residual data.
Plot Residual Terstandarisasi
# Residual terstandarisasi
res_std <- residuals(garch12, standardize = TRUE)
# Plot residual
plot(res_std, type = "l", col = "blue",
main = "Standardized Residuals GARCH(1,2)",
ylab = "Residual", xlab = "waktu")
abline(h = 0, col = "red", lty = 2)Plot ACF dari Residual Terstandarisasi
QQ-Plot Residual Terstandarisasi
Uji Ljung–Box (Autokorelasi Residual)
##
## Box-Ljung test
##
## data: res_std
## X-squared = 42.951, df = 30, p-value = 0.05918
Jika p-value > 0.05 → tidak ada autokorelasi. ✅ Model sudah mampu menangkap dinamika mean dengan baik.
Uji ARCH–LM (Efek ARCH Tersisa)
##
## ARCH LM-test; Null hypothesis: no ARCH effects
##
## data: res_std
## Chi-squared = 15.556, df = 30, p-value = 0.9862
hasil uji ARCH ulang ( p-value = 0.9862 > 0.05), maka: Tidak ada lagi efek ARCH yang signifikan pada residual model GARCH(1,2), menandakan model GARCH(1,2) telah berhasil menangkap dinamika volatilitas data.
# MODEL ARIMA(1,1,1) — Nilai Aktual vs Dugaan
library(forecast)
# Fit model ARIMA
fit_arima <- Arima(train_ts, order = c(1,1,1))
# Dugaan (fitted values)
dugaan_arima <- fitted(fit_arima)
# Gabungkan dengan nilai aktual
df_dugaan_arima <- data.frame(
Periode = time(train_ts),
Aktual = as.numeric(train_ts),
Dugaan_ARIMA = as.numeric(dugaan_arima)
)
head(df_dugaan_arima, 10)## Periode Aktual Dugaan_ARIMA
## 1 1 12536 12523.46
## 2 2 12652 12537.50
## 3 3 12721 12668.79
## 4 4 12796 12737.71
## 5 5 12795 12813.39
## 6 6 12703 12802.64
## 7 7 12631 12694.26
## 8 8 12671 12617.58
## 9 9 12643 12670.29
## 10 10 12680 12638.98
# Fitted (dugaan) dari model ARIMA
fit_arima <- Arima(train_ts, order = c(1,1,1))
dugaan_arima <- fitted(fit_arima)
# Plot nilai aktual vs dugaan
par(mfrow = c(1,1))
plot(train_ts, type = "l", col = "black", lwd = 2,
xlab = "Waktu", ylab = "Kurs Jual (Rp/USD)",
main = "Nilai Aktual vs Dugaan ARIMA(1,1,1)")
lines(dugaan_arima, col = "skyblue", lwd = 2)
legend("topleft", legend = c("Aktual", "Dugaan ARIMA"),
col = c("black", "skyblue"), lty = 1, lwd = 2, bty = "n")
Berdasarkan plot data aktual vs dugaan di atas terlihat bahwa nilai dan
pola dugaan yang dihasilkan dari model ARIMA (1,1,1) mendekati nilai
aktualnya.
# Forecast ARIMA(1,1,1)
fc_plot <- forecast(fit_arima, h=length(test_df$Kurs_Jual))
plot(fc_plot,
main = "Peramalan Model ARIMA(1,1,1)",
ylab = "Kurs Jual", xlab = "waktu")# MODEL ARIMA(1,1,1)-GARCH(1,2) — Nilai Aktual vs Dugaan
library(rugarch)
library(xts)
# Spesifikasi model
spec_garch <- ugarchspec(
variance.model = list(model = "sGARCH", garchOrder = c(1,2)),
mean.model = list(armaOrder = c(1,1), include.mean = TRUE),
distribution.model = "norm"
)
# Estimasi model
fit_garch <- ugarchfit(spec_garch, data = train_xts)
# Fitted (dugaan)
dugaan_garch <- fitted(fit_garch)
# aktual & dugaan dalam dataframe
df_dugaan_garch <- data.frame(
Tanggal = index(train_xts),
Aktual = as.numeric(train_xts),
Dugaan_ARIMA_GARCH = as.numeric(dugaan_garch)
)
head(df_dugaan_garch, 10)## Tanggal Aktual Dugaan_ARIMA_GARCH
## 1 2015-01-02 12536 12527.24
## 2 2015-01-05 12652 12536.64
## 3 2015-01-06 12721 12660.39
## 4 2015-01-07 12796 12725.41
## 5 2015-01-08 12795 12801.13
## 6 2015-01-09 12703 12794.55
## 7 2015-01-12 12631 12696.34
## 8 2015-01-13 12671 12626.25
## 9 2015-01-14 12643 12674.25
## 10 2015-01-15 12680 12640.73
# FIT MODEL ARIMA(1,1,1)-GARCH(1,2)
library(rugarch)
# Spesifikasi model
spec_garch <- ugarchspec(
variance.model = list(model = "sGARCH", garchOrder = c(1,2)),
mean.model = list(armaOrder = c(1,1), include.mean = TRUE),
distribution.model = "norm"
)
# Estimasi model pada data training (xts)
fit_garch <- ugarchfit(spec_garch, data = train_xts)
dugaan_garch <- fitted(fit_garch)
dugaan_garch_xts <- xts(as.numeric(dugaan_garch), order.by = index(train_xts))
# Plot nilai aktual vs dugaan
plot(train_xts, type = "l", col = "black", lwd = 2,
xlab = "Tanggal", ylab = "Kurs Jual",
main = "Nilai Aktual vs Dugaan ARIMA(1,1,1)-GARCH(1,2)")lines(dugaan_garch_xts, col = "orange", lwd = 2)
legend("topleft", legend = c("Aktual", "Dugaan ARIMA-GARCH"),
col = c("black", "orange"), lty = 1, lwd = 2, bty = "n")Perbandingan Ramalan ARIMA(1,1,1)-GARCH(1,2) dengan Data Aktual
# Forecast sepanjang test set
h <- length(test_df$Kurs_Jual)
# Forecast GARCH untuk periode test
fc_garch <- ugarchforecast(fit_garch, n.ahead = h)
fc_mean <- as.numeric(fitted(fc_garch))
fc_sigma <- as.numeric(sigma(fc_garch))
# Tanggal ramalan = tanggal di test set
future_dates <- as.Date(test_df$Date)
# Plot aktual vs hasil ramalan
plot(index(train_xts), train_xts, type = "l", col = "black", lwd = 2,
xlab = "Tahun", ylab = "Kurs Jual",
main = "Perbandingan Ramalan ARIMA(1,1,1)-GARCH(1,2) dengan Data Aktual")
lines(future_dates, fc_mean, col = "orange", lwd = 2)
lines(future_dates, fc_mean + 1.96*fc_sigma, col = "red", lty = 2)
lines(future_dates, fc_mean - 1.96*fc_sigma, col = "red", lty = 2)
lines(future_dates, test_df$Kurs_Jual, col = "blue", lwd = 2)
legend("topleft",
legend = c("Training", "Data Test Aktual", "Ramalan", "95% Interval"),
col = c("black", "blue", "orange", "red"),
lty = c(1,1,1,2), lwd = 2, bty = "n")library(forecast)
library(rugarch)
library(Metrics)
# MODEL 1: ARIMA(1,1,1)
fit_arima <- Arima(train_ts, order = c(1,1,1))
fc_arima <- forecast(fit_arima, h = length(test_ts))
# MODEL 2: ARIMA(1,1,1)-GARCH(1,2)
library(rugarch)
spec_garch <- ugarchspec(
variance.model = list(model = "sGARCH", garchOrder = c(1,2)),
mean.model = list(armaOrder = c(1,1), include.mean = TRUE),
distribution.model = "norm"
)
fit_garch <- ugarchfit(spec = spec_garch, data = train_xts)
fc_garch <- ugarchforecast(fit_garch, n.ahead = length(test_xts))
fc_garch_values <- as.numeric(fitted(fc_garch))
# Evaluasi AIC dan BIC
aic_bic <- data.frame(
Model = c("ARIMA(1,1,1)", "ARIMA(1,1,1)-GARCH(1,2)"),
AIC = c(AIC(fit_arima), infocriteria(fit_garch)[1]),
BIC = c(BIC(fit_arima), infocriteria(fit_garch)[2])
)
aic_bic## Model AIC BIC
## 1 ARIMA(1,1,1) 23127.30767 23144.25245
## 2 ARIMA(1,1,1)-GARCH(1,2) 10.66838 10.68722
# Evaluasi akurasi model
eval_metrics <- data.frame(
Model = c("ARIMA(1,1,1)", "ARIMA(1,1,1)-GARCH(1,2)"),
RMSE = c(rmse(as.numeric(test_ts), as.numeric(fc_arima$mean)),
rmse(as.numeric(test_ts), as.numeric(fc_garch_values))),
MAE = c(mae(as.numeric(test_ts), as.numeric(fc_arima$mean)),
mae(as.numeric(test_ts), as.numeric(fc_garch_values))),
MAPE = c(mape(as.numeric(test_ts), as.numeric(fc_arima$mean)),
mape(as.numeric(test_ts), as.numeric(fc_garch_values)))
)
eval_metrics## Model RMSE MAE MAPE
## 1 ARIMA(1,1,1) 1127.478 1035.981 0.06392005
## 2 ARIMA(1,1,1)-GARCH(1,2) 1101.809 1008.060 0.06217679