Packages

library(quantmod)
library(tidyverse)
library(tseries)
library(FinTS)
library(rugarch)
library(forecast)
library(scales)

Data Preparation

getSymbols(
  "AAPL",
  src = "yahoo",
  from = "2022-04-11",
  to = "2026-10-01",
  auto.assign = TRUE
)
## [1] "AAPL"
head(AAPL)
##            AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume AAPL.Adjusted
## 2022-04-11    168.71    169.03   165.50     165.75    72246700      162.1100
## 2022-04-12    168.02    169.87   166.64     167.66    79265200      163.9781
## 2022-04-13    167.39    171.04   166.77     170.40    70618900      166.6579
## 2022-04-14    170.62    171.27   165.04     165.29    75329400      161.6601
## 2022-04-18    163.92    166.60   163.57     165.07    69023900      161.4450
## 2022-04-19    165.02    167.82   163.91     167.40    67723800      163.7237
tail(AAPL)
##            AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume AAPL.Adjusted
## 2026-09-23    341.08    341.80   335.50     337.02    31658800        337.02
## 2026-09-24    336.72    338.91   334.30     335.92    24733100        335.92
## 2026-09-25    336.04    341.67   334.53     341.07    30002500        341.07
## 2026-09-28    340.37    342.99   338.04     338.40    32820800        338.40
## 2026-09-29    336.97    337.09   328.70     329.40    38478000        329.40
## 2026-09-30    330.80    339.50   330.14     333.02    49931500        333.02
start(AAPL)
## [1] "2022-04-11"
end(AAPL)
## [1] "2026-09-30"

Closing Price

data_AAPL <- data.frame(
  Date = index(AAPL),
  Close = as.numeric(Cl(AAPL))
)

Checking Missing Value

sum(is.na(data_AAPL$Close))
## [1] 0
head(data_AAPL)
##         Date  Close
## 1 2022-04-11 165.75
## 2 2022-04-12 167.66
## 3 2022-04-13 170.40
## 4 2022-04-14 165.29
## 5 2022-04-18 165.07
## 6 2022-04-19 167.40
tail(data_AAPL)
##            Date  Close
## 1117 2026-09-23 337.02
## 1118 2026-09-24 335.92
## 1119 2026-09-25 341.07
## 1120 2026-09-28 338.40
## 1121 2026-09-29 329.40
## 1122 2026-09-30 333.02

Plot closing price saham AAPL

ggplot(data_AAPL, aes(x = Date, y = Close)) +
  geom_line() +
  labs(
    title = "Pergerakan Harga Saham APPLE INC.",
    x = "Tanggal",
    y = "Harga Penutupan (Rp)"
  ) +
  theme_minimal()

## Data Transformation (return log)

data_AAPL <- data_AAPL %>%
  mutate(
    LogReturn = c(NA, diff(log(Close)))
  ) %>%
  drop_na(LogReturn)

head(data_AAPL)
##         Date  Close    LogReturn
## 1 2022-04-12 167.66  0.011457512
## 2 2022-04-13 170.40  0.016210438
## 3 2022-04-14 165.29 -0.030447112
## 4 2022-04-18 165.07 -0.001331796
## 5 2022-04-19 167.40  0.014016451
## 6 2022-04-20 167.23 -0.001016037
#PLOT LOG RETURN
ggplot(data_AAPL, aes(x = Date, y = LogReturn)) +
  geom_line() +
  labs(
    title = "Log Return Saham AAPL",
    x = "Tanggal",
    y = "Log Return"
  ) +
  theme_minimal()

## Statistik Deskriptif

summary(data_AAPL$LogReturn)
##       Min.    1st Qu.     Median       Mean    3rd Qu.       Max. 
## -0.0970133 -0.0076099  0.0011396  0.0006224  0.0095100  0.1426175
# Statistik tambahan
sd(data_AAPL$LogReturn)
## [1] 0.01767914
mean(data_AAPL$LogReturn)
## [1] 0.0006224102
min(data_AAPL$LogReturn)
## [1] -0.09701334
max(data_AAPL$LogReturn)
## [1] 0.1426175

Train-Test Splitting

n <- nrow(data_AAPL)
n_train <- floor(0.80 * n)

train <- data_AAPL[1:n_train, ]
test <- data_AAPL[(n_train + 1):n, ]

cat("Jumlah data keseluruhan :", n, "\n")
## Jumlah data keseluruhan : 1121
cat("Jumlah data training   :", nrow(train), "\n")
## Jumlah data training   : 896
cat("Jumlah data testing    :", nrow(test), "\n")
## Jumlah data testing    : 225
cat(
  "Periode training       :",
  as.character(min(train$Date)),
  "sampai",
  as.character(max(train$Date)),
  "\n"
)
## Periode training       : 2022-04-12 sampai 2025-11-05
cat(
  "Periode testing        :",
  as.character(min(test$Date)),
  "sampai",
  as.character(max(test$Date)),
  "\n"
)
## Periode testing        : 2025-11-06 sampai 2026-09-30
return_train <- train$LogReturn
return_test <- test$LogReturn

UJI STASIONERITAS ADF

adf_result <- adf.test(return_train)
adf_result
## 
##  Augmented Dickey-Fuller Test
## 
## data:  return_train
## Dickey-Fuller = -10.344, Lag order = 9, p-value = 0.01
## alternative hypothesis: stationary

Hasil uji Augmented Dickey-Fuller (ADF) pada data return pelatihan (return_train) menghasilkan nilai statistik Dickey-Fuller sebesar -10,344 dengan p-value < 0,01. Karena p-value lebih kecil dari tingkat signifikansi 0,05 (alpha = 5%), maka hipotesis nol ditolak. Hal ini menunjukkan bahwa data return saham AAPL telah stasioner dan siap digunakan untuk pemodelan lebih lanjut.

ACF DAN PACF RETURN

par(mfrow = c(1, 2))
acf(return_train,main = "ACF Return Saham AAPL")
pacf(return_train,main = "PACF Return Saham AAPL")

par(mfrow = c(1, 1))

MODEL MEAN DENGAN AUTO ARIMA

model_arma <- auto.arima(
  return_train,
  seasonal = FALSE,
  stationary = TRUE,
  stepwise = FALSE,
  approximation = FALSE
)

summary(model_arma)
## Series: return_train 
## ARIMA(0,0,0) with zero mean 
## 
## sigma^2 = 0.0003283:  log likelihood = 2322.3
## AIC=-4642.6   AICc=-4642.59   BIC=-4637.8
## 
## Training set error measures:
##                       ME       RMSE        MAE MPE MAPE      MASE       ACF1
## Training set 0.000545156 0.01811906 0.01266665 100  100 0.7081714 0.01030677
# Orde ARIMA
arimaorder(model_arma)
## p d q 
## 0 0 0

Model ARIMA(0,0,0) dengan zero mean menunjukkan bahwa tidak ditemukan struktur AR maupun MA yang diperlukan untuk menjelaskan dinamika rata-rata return.

Dengan kata lain, berdasarkan prosedur auto.arima, return tidak memiliki struktur autoregresif/Moving Average yang kuat pada bagian mean equation.

Oleh karena itu, model mean yang digunakan sangat sederhana.

RESIDUAL MODEL MEAN

resid_arma <- residuals(model_arma)
resid_arma <- na.omit(resid_arma)

# Plot residual
plot(
  resid_arma,
  type = "l",
  main = "Residual Model Mean",
  xlab = "Observasi",
  ylab = "Residual"
)

Residual merupakan bagian return yang belum dijelaskan oleh model mean. Residual inilah yang kemudian diperiksa apakah memiliki pola volatilitas yang berubah-ubah. Jika residual mengandung ARCH effect, maka model mean saja belum cukup dan perlu model volatilitas seperti GARCH/EGARCH.

GARCH Modelling

UJI ARCH-LM

arch_test <- ArchTest(resid_arma,lags = 12)
arch_test
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  resid_arma
## Chi-squared = 111.08, df = 12, p-value < 2.2e-16

Karena nilai < 2,2 ^-16 jauh lebih kecil dari tingkat signifikansi alpha = 0,05 (5%), maka keputusan ujinya adalah Tolak H0. Kesimpulan Akhir:Data residual dari model ARMA (resid_arma) terbukti secara signifikan mengandung efek ARCH (heteroskedastisitas). Residual memiliki efek ARCH yang signifikan. Artinya varians/volatilitas return tidak konstan dari waktu ke waktu. Ini merupakan alasan statistik yang kuat untuk melanjutkan ke model GARCH-family.

PLOT ACF DAN PACF RESIDUAL KUADRAT

resid_kuadrat <- resid_arma^2
par(mfrow = c(1, 2))
acf(resid_kuadrat,main = "ACF Residual Kuadrat")

pacf(resid_kuadrat,main = "PACF Residual Kuadrat")

par(mfrow = c(1, 1))

Pengkuadratan residual digunakan untuk melihat ketergantungan pada varians/volatilitas. Jika residual kuadrat memiliki autokorelasi, berarti volatilitas pada suatu periode berkaitan dengan volatilitas periode sebelumnya. Hal tersebut merupakan karakteristik yang ingin ditangkap oleh GARCH/EGARCH.

MODEL GARCH(1,1)

spec_garch <- ugarchspec(
  
  variance.model = list(
    model = "sGARCH",
    garchOrder = c(1, 1)
  ),
  
  mean.model = list(
    armaOrder = c(
      arimaorder(model_arma)[1],
      arimaorder(model_arma)[3]
    ),
    include.mean = TRUE
  ),
  
  distribution.model = "std"
)

ESTIMASI MODEL GARCH(1,1)

fit_garch <- ugarchfit(
  spec = spec_garch,
  data = return_train
)

show(fit_garch)
## 
## *---------------------------------*
## *          GARCH Model Fit        *
## *---------------------------------*
## 
## Conditional Variance Dynamics    
## -----------------------------------
## GARCH Model  : sGARCH(1,1)
## Mean Model   : ARFIMA(0,0,0)
## Distribution : std 
## 
## Optimal Parameters
## ------------------------------------
##         Estimate  Std. Error  t value Pr(>|t|)
## mu      0.001080    0.000457   2.3604 0.018254
## omega   0.000008    0.000004   2.2231 0.026207
## alpha1  0.069817    0.005353  13.0439 0.000000
## beta1   0.907927    0.014542  62.4359 0.000000
## shape   4.335644    0.548825   7.8999 0.000000
## 
## Robust Standard Errors:
##         Estimate  Std. Error  t value Pr(>|t|)
## mu      0.001080    0.000477   2.2650 0.023513
## omega   0.000008    0.000007   1.2210 0.222078
## alpha1  0.069817    0.018744   3.7248 0.000195
## beta1   0.907927    0.016362  55.4884 0.000000
## shape   4.335644    0.658295   6.5862 0.000000
## 
## LogLikelihood : 2437.761 
## 
## Information Criteria
## ------------------------------------
##                     
## Akaike       -5.4303
## Bayes        -5.4035
## Shibata      -5.4303
## Hannan-Quinn -5.4200
## 
## Weighted Ljung-Box Test on Standardized Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                      1.549  0.2133
## Lag[2*(p+q)+(p+q)-1][2]     1.552  0.3492
## Lag[4*(p+q)+(p+q)-1][5]     2.157  0.5817
## d.o.f=0
## H0 : No serial correlation
## 
## Weighted Ljung-Box Test on Standardized Squared Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                      3.643 0.05630
## Lag[2*(p+q)+(p+q)-1][5]     7.418 0.04101
## Lag[4*(p+q)+(p+q)-1][9]    11.035 0.02986
## d.o.f=2
## 
## Weighted ARCH LM Tests
## ------------------------------------
##             Statistic Shape Scale P-Value
## ARCH Lag[3]  0.001495 0.500 2.000 0.96916
## ARCH Lag[5]  6.623725 1.440 1.667 0.04279
## ARCH Lag[7]  8.539669 2.315 1.543 0.03974
## 
## Nyblom stability test
## ------------------------------------
## Joint Statistic:  3.2153
## Individual Statistics:              
## mu     0.05912
## omega  0.87645
## alpha1 0.72877
## beta1  0.67622
## shape  1.19971
## 
## Asymptotic Critical Values (10% 5% 1%)
## Joint Statistic:          1.28 1.47 1.88
## Individual Statistic:     0.35 0.47 0.75
## 
## Sign Bias Test
## ------------------------------------
##                    t-value    prob sig
## Sign Bias           0.2040 0.83838    
## Negative Sign Bias  2.1584 0.03116  **
## Positive Sign Bias  0.5529 0.58048    
## Joint Effect        5.7843 0.12259    
## 
## 
## Adjusted Pearson Goodness-of-Fit Test:
## ------------------------------------
##   group statistic p-value(g-1)
## 1    20     22.62       0.2547
## 2    30     19.40       0.9105
## 3    40     42.84       0.3099
## 4    50     41.95       0.7523
## 
## 
## Elapsed time : 0.266021

GARCH(1,1) memodelkan varians bersyarat berdasarkan:

konstanta volatilitas, kuadrat shock periode sebelumnya, volatilitas periode sebelumnya.

Distribusi std (Student-t) digunakan untuk mengakomodasi return yang memiliki kemungkinan ekor distribusi lebih tebal dibandingkan distribusi normal.

PARAMETER MODEL GARCH

coef_garch <- coef(fit_garch)
coef_garch
##           mu        omega       alpha1        beta1        shape 
## 1.079575e-03 8.010634e-06 6.981734e-02 9.079271e-01 4.335644e+00
# Matriks koefisien
fit_garch@fit$matcoef
##            Estimate   Std. Error   t value     Pr(>|t|)
## mu     1.079575e-03 4.573638e-04  2.360429 1.825381e-02
## omega  8.010634e-06 3.603313e-06  2.223130 2.620702e-02
## alpha1 6.981734e-02 5.352507e-03 13.043858 0.000000e+00
## beta1  9.079271e-01 1.454175e-02 62.435908 0.000000e+00
## shape  4.335644e+00 5.488246e-01  7.899871 2.886580e-15

μ = 0,001080 Menunjukkan estimasi rata-rata return bersyarat.

α₁ = 0,069817 Menggambarkan respons volatilitas terhadap shock baru pada return.

β₁ = 0,907927 Menunjukkan persistensi volatilitas. Nilai yang tinggi menunjukkan bahwa volatilitas masa lalu memiliki pengaruh kuat terhadap volatilitas berikutnya.

Secara kasar: \[ \alpha_1+\beta_1 =0,069817+0,907927 \approx0,9777 \] Nilai yang mendekati 1 menunjukkan persistensi volatilitas yang tinggi.

INFORMATION CRITERIA GARCH

infocriteria(fit_garch)
##                       
## Akaike       -5.430271
## Bayes        -5.403497
## Shibata      -5.430333
## Hannan-Quinn -5.420041

UJI LEVERAGE / ASIMETRI. SIGN BIAS TEST ENGLE-NG

signbias_garch<-signbias(fit_garch)
signbias_garch
##                      t-value       prob sig
## Sign Bias          0.2040245 0.83838098    
## Negative Sign Bias 2.1583826 0.03116459  **
## Positive Sign Bias 0.5528850 0.58048082    
## Joint Effect       5.7843221 0.12258813

Hasil menunjukkan bahwa residual standar tidak memiliki serial correlation yang signifikan berdasarkan Ljung-Box pada beberapa lag, tetapi residual kuadrat masih menunjukkan beberapa p-value di bawah 0,05, dan ARCH LM pada lag 5 dan 7 juga signifikan.

Ini menunjukkan bahwa GARCH(1,1) belum sepenuhnya menghilangkan seluruh dinamika volatilitas.

Selain itu, Sign Bias Test menunjukkan:

\[ p_{\text{Negative Sign Bias}}=0,03116 \]

yang signifikan.

Artinya terdapat indikasi bahwa shock negatif memberikan pola pengaruh yang berbeda terhadap volatilitas yang belum sepenuhnya ditangkap oleh GARCH standar.

Temuan ini menjadi motivasi untuk mencoba EGARCH, karena EGARCH secara eksplisit memungkinkan adanya efek asimetris/leverage.

EGARCH Modelling

SPESIFIKASI MODEL EGARCH(1,1)

spec_egarch <- ugarchspec(
  
  variance.model = list(
    model = "eGARCH",
    garchOrder = c(1, 1)
  ),
  
  mean.model = list(
    armaOrder = c(
      arimaorder(model_arma)[1],
      arimaorder(model_arma)[3]
    ),
    include.mean = TRUE
  ),
  
  distribution.model = "std"
)

ESTIMASI MODEL EGARCH(1,1)

fit_egarch <- ugarchfit(
  spec = spec_egarch,
  data = return_train
)

show(fit_egarch)
## 
## *---------------------------------*
## *          GARCH Model Fit        *
## *---------------------------------*
## 
## Conditional Variance Dynamics    
## -----------------------------------
## GARCH Model  : eGARCH(1,1)
## Mean Model   : ARFIMA(0,0,0)
## Distribution : std 
## 
## Optimal Parameters
## ------------------------------------
##         Estimate  Std. Error  t value Pr(>|t|)
## mu       0.00074    0.000424   1.7452 0.080947
## omega   -0.24797    0.021469 -11.5502 0.000000
## alpha1  -0.10017    0.023454  -4.2709 0.000019
## beta1    0.97000    0.002750 352.7281 0.000000
## gamma1   0.12424    0.045988   2.7015 0.006902
## shape    4.70718    0.693244   6.7901 0.000000
## 
## Robust Standard Errors:
##         Estimate  Std. Error  t value Pr(>|t|)
## mu       0.00074    0.000515   1.4373 0.150639
## omega   -0.24797    0.039287  -6.3118 0.000000
## alpha1  -0.10017    0.028040  -3.5724 0.000354
## beta1    0.97000    0.004870 199.1666 0.000000
## gamma1   0.12424    0.068184   1.8221 0.068445
## shape    4.70718    0.718040   6.5556 0.000000
## 
## LogLikelihood : 2445.478 
## 
## Information Criteria
## ------------------------------------
##                     
## Akaike       -5.4453
## Bayes        -5.4131
## Shibata      -5.4454
## Hannan-Quinn -5.4330
## 
## Weighted Ljung-Box Test on Standardized Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                      1.860  0.1727
## Lag[2*(p+q)+(p+q)-1][2]     1.864  0.2863
## Lag[4*(p+q)+(p+q)-1][5]     2.179  0.5767
## d.o.f=0
## H0 : No serial correlation
## 
## Weighted Ljung-Box Test on Standardized Squared Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                      6.128 0.01330
## Lag[2*(p+q)+(p+q)-1][5]     8.201 0.02627
## Lag[4*(p+q)+(p+q)-1][9]    10.363 0.04192
## d.o.f=2
## 
## Weighted ARCH LM Tests
## ------------------------------------
##             Statistic Shape Scale P-Value
## ARCH Lag[3]    0.3092 0.500 2.000  0.5782
## ARCH Lag[5]    3.1589 1.440 1.667  0.2675
## ARCH Lag[7]    4.5914 2.315 1.543  0.2692
## 
## Nyblom stability test
## ------------------------------------
## Joint Statistic:  2.5339
## Individual Statistics:              
## mu     0.07716
## omega  0.26748
## alpha1 0.24290
## beta1  0.22937
## gamma1 0.05075
## shape  0.98750
## 
## Asymptotic Critical Values (10% 5% 1%)
## Joint Statistic:          1.49 1.68 2.12
## Individual Statistic:     0.35 0.47 0.75
## 
## Sign Bias Test
## ------------------------------------
##                    t-value   prob sig
## Sign Bias          0.09105 0.9275    
## Negative Sign Bias 1.54963 0.1216    
## Positive Sign Bias 1.33625 0.1818    
## Joint Effect       4.31478 0.2294    
## 
## 
## Adjusted Pearson Goodness-of-Fit Test:
## ------------------------------------
##   group statistic p-value(g-1)
## 1    20     27.39      0.09583
## 2    30     30.99      0.36606
## 3    40     42.93      0.30654
## 4    50     59.58      0.14308
## 
## 
## Elapsed time : 0.3226631

PARAMETER MODEL EGARCH

coef_egarch <- coef(fit_egarch)
coef_egarch
##            mu         omega        alpha1         beta1        gamma1 
##  0.0007404735 -0.2479682618 -0.1001682450  0.9700029298  0.1242362994 
##         shape 
##  4.7071852760
# Matriks koefisien
fit_egarch@fit$matcoef
##             Estimate   Std. Error    t value     Pr(>|t|)
## mu      0.0007404735 0.0004242866   1.745220 8.094653e-02
## omega  -0.2479682618 0.0214688281 -11.550154 0.000000e+00
## alpha1 -0.1001682450 0.0234535893  -4.270913 1.946741e-05
## beta1   0.9700029298 0.0027500017 352.728115 0.000000e+00
## gamma1  0.1242362994 0.0459876761   2.701513 6.902481e-03
## shape   4.7071852760 0.6932437264   6.790087 1.120659e-11

INFORMATION CRITERIA EGARCH

infocriteria(fit_egarch)
##                       
## Akaike       -5.445262
## Bayes        -5.413133
## Shibata      -5.445351
## Hannan-Quinn -5.432986

Model Comparison

comparison_model <- data.frame(
  
  Model = c(
    "GARCH(1,1)",
    "EGARCH(1,1)"
  ),
  
  AIC = c(
    infocriteria(fit_garch)[1],
    infocriteria(fit_egarch)[1]
  ),
  
  BIC = c(
    infocriteria(fit_garch)[2],
    infocriteria(fit_egarch)[2]
  ),
  
  LogLikelihood = c(
    likelihood(fit_garch),
    likelihood(fit_egarch)
  )
)

comparison_model
##         Model       AIC       BIC LogLikelihood
## 1  GARCH(1,1) -5.430271 -5.403497      2437.761
## 2 EGARCH(1,1) -5.445262 -5.413133      2445.478

PARAMETER ASIMETRI EGARCH

egarch_coef <- fit_egarch@fit$matcoef
gamma_result <- egarch_coef[5, 1:4] # Baris ke-5 (gamma1), kolom 1 sampai 4
gamma_result
##    Estimate  Std. Error     t value    Pr(>|t|) 
## 0.124236299 0.045987676 2.701512883 0.006902481

EGARCH Diagnostics

Residual terstandarisasi

std_resid_egarch <- residuals(
  fit_egarch,
  standardize = TRUE
)

std_resid_egarch <- as.numeric(
  na.omit(std_resid_egarch)
)

Plot residual terstandarisasi

plot(
  std_resid_egarch,
  type = "l",
  main = "Residual Terstandarisasi EGARCH",
  xlab = "Observasi",
  ylab = "Residual Terstandarisasi"
)

ACF residual terstandarisasi

acf(
  std_resid_egarch,
  main = "ACF Residual Terstandarisasi EGARCH"
)

### ACF residual kuadrat

acf(
  std_resid_egarch^2,
  main = "ACF Kuadrat Residual Terstandarisasi EGARCH"
)

### UJI LJUNG-BOX RESIDUAL

ljung_box_residual <- Box.test(
  std_resid_egarch,
  lag = 10,
  type = "Ljung-Box"
)

ljung_box_residual
## 
##  Box-Ljung test
## 
## data:  std_resid_egarch
## X-squared = 5.9154, df = 10, p-value = 0.8223

Residual terstandarisasi tidak menunjukkan autokorelasi yang signifikan. Ini mendukung bahwa model sudah cukup baik dalam menangkap dinamika mean.

UJI LJUNG-BOX RESIDUAL KUADRAT

ljung_box_squared <- Box.test(
  std_resid_egarch^2,
  lag = 10,
  type = "Ljung-Box"
)

ljung_box_squared
## 
##  Box-Ljung test
## 
## data:  std_resid_egarch^2
## X-squared = 15.474, df = 10, p-value = 0.1157

Dinamika volatilitas yang tersisa tidak menunjukkan autokorelasi yang signifikan berdasarkan uji ini.

UJI ARCH-LM PADA RESIDUAL EGARCH

arch_test_egarch <- ArchTest(
  std_resid_egarch,
  lags = 12
)

arch_test_egarch
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  std_resid_egarch
## Chi-squared = 16.305, df = 12, p-value = 0.1777

Tidak terdapat bukti yang cukup bahwa residual terstandarisasi masih mengandung efek ARCH.

SIGN BIAS TEST PADA MODEL EGARCH

signbias_egarch <- signbias(fit_egarch)
signbias_egarch
##                       t-value      prob sig
## Sign Bias          0.09105241 0.9274714    
## Negative Sign Bias 1.54962551 0.1215865    
## Positive Sign Bias 1.33625222 0.1818080    
## Joint Effect       4.31478442 0.2294181

Artinya tidak terdapat bukti statistik bahwa residual EGARCH masih mengandung efek sign bias yang signifikan.

JARQUE-BERA RESIDUAL

jarque_bera_egarch <- jarque.bera.test(
  std_resid_egarch
)

jarque_bera_egarch
## 
##  Jarque Bera Test
## 
## data:  std_resid_egarch
## X-squared = 254.92, df = 2, p-value < 2.2e-16

Forecasting

FORECAST RETURN DAN VOLATILITAS

n_forecast <- nrow(test)

forecast_egarch <- ugarchforecast(
  fit_egarch,
  n.ahead = n_forecast
)

#mengambil hasil forecast return
return_forecast <- as.numeric(
  fitted(forecast_egarch)
)
return_forecast
##   [1] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##   [6] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [11] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [16] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [21] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [26] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [31] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [36] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [41] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [46] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [51] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [56] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [61] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [66] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [71] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [76] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [81] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [86] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [91] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
##  [96] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [101] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [106] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [111] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [116] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [121] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [126] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [131] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [136] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [141] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [146] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [151] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [156] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [161] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [166] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [171] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [176] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [181] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [186] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [191] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [196] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [201] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [206] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [211] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [216] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735
## [221] 0.0007404735 0.0007404735 0.0007404735 0.0007404735 0.0007404735

Hal ini terjadi karena model mean yang diperoleh adalah ARIMA(0,0,0). Jadi, ketika melakukan forecast multi-step, prediksi return cenderung menuju nilai mean model.

Ini bukan berarti model memprediksi return aktual akan selalu sama; melainkan nilai ekspektasi return bersyarat untuk horizon tersebut konstan.

#mengambil hasil forecast volatilitas
vol_forecast <- as.numeric(
  sigma(forecast_egarch)
)
vol_forecast
##   [1] 0.01199545 0.01210026 0.01220280 0.01230310 0.01240118 0.01249706
##   [7] 0.01259077 0.01268235 0.01277181 0.01285919 0.01294453 0.01302784
##  [13] 0.01310917 0.01318854 0.01326599 0.01334155 0.01341526 0.01348714
##  [19] 0.01355724 0.01362558 0.01369220 0.01375714 0.01382042 0.01388208
##  [25] 0.01394215 0.01400067 0.01405767 0.01411318 0.01416723 0.01421986
##  [31] 0.01427110 0.01432098 0.01436953 0.01441678 0.01446276 0.01450750
##  [37] 0.01455103 0.01459338 0.01463458 0.01467466 0.01471363 0.01475154
##  [43] 0.01478840 0.01482424 0.01485910 0.01489298 0.01492592 0.01495795
##  [49] 0.01498908 0.01501933 0.01504874 0.01507732 0.01510510 0.01513209
##  [55] 0.01515832 0.01518380 0.01520856 0.01523262 0.01525599 0.01527869
##  [61] 0.01530075 0.01532217 0.01534298 0.01536319 0.01538283 0.01540189
##  [67] 0.01542041 0.01543840 0.01545586 0.01547282 0.01548929 0.01550528
##  [73] 0.01552081 0.01553588 0.01555052 0.01556473 0.01557853 0.01559193
##  [79] 0.01560493 0.01561756 0.01562981 0.01564171 0.01565326 0.01566447
##  [85] 0.01567535 0.01568592 0.01569617 0.01570612 0.01571578 0.01572516
##  [91] 0.01573426 0.01574309 0.01575166 0.01575998 0.01576805 0.01577589
##  [97] 0.01578349 0.01579087 0.01579803 0.01580498 0.01581173 0.01581827
## [103] 0.01582462 0.01583079 0.01583677 0.01584257 0.01584820 0.01585366
## [109] 0.01585897 0.01586411 0.01586910 0.01587395 0.01587864 0.01588320
## [115] 0.01588763 0.01589192 0.01589609 0.01590013 0.01590405 0.01590785
## [121] 0.01591155 0.01591513 0.01591860 0.01592197 0.01592524 0.01592842
## [127] 0.01593149 0.01593448 0.01593738 0.01594019 0.01594292 0.01594556
## [133] 0.01594813 0.01595062 0.01595304 0.01595538 0.01595766 0.01595986
## [139] 0.01596200 0.01596408 0.01596609 0.01596805 0.01596994 0.01597178
## [145] 0.01597357 0.01597530 0.01597698 0.01597861 0.01598019 0.01598172
## [151] 0.01598321 0.01598465 0.01598605 0.01598741 0.01598872 0.01599000
## [157] 0.01599124 0.01599244 0.01599361 0.01599474 0.01599584 0.01599690
## [163] 0.01599793 0.01599894 0.01599991 0.01600085 0.01600177 0.01600265
## [169] 0.01600351 0.01600435 0.01600516 0.01600594 0.01600671 0.01600744
## [175] 0.01600816 0.01600886 0.01600953 0.01601019 0.01601082 0.01601144
## [181] 0.01601203 0.01601261 0.01601318 0.01601372 0.01601425 0.01601476
## [187] 0.01601526 0.01601574 0.01601621 0.01601667 0.01601711 0.01601754
## [193] 0.01601795 0.01601835 0.01601874 0.01601912 0.01601949 0.01601985
## [199] 0.01602019 0.01602053 0.01602085 0.01602117 0.01602147 0.01602177
## [205] 0.01602206 0.01602234 0.01602261 0.01602287 0.01602312 0.01602337
## [211] 0.01602361 0.01602384 0.01602407 0.01602429 0.01602450 0.01602471
## [217] 0.01602491 0.01602510 0.01602529 0.01602547 0.01602565 0.01602582
## [223] 0.01602599 0.01602615 0.01602630

Model memperkirakan volatilitas akan meningkat secara bertahap ketika horizon forecasting semakin jauh.

Hal ini menunjukkan adanya proses mean reversion menuju tingkat volatilitas jangka panjang yang dihasilkan model.

KONVERSI FORECAST RETURN MENJADI FORECAST HARGA

harga_terakhir <- tail(
  train$Close,
  1
)

harga_forecast <- harga_terakhir *
  exp(cumsum(return_forecast))

Karena model memprediksi log return, hasil tersebut harus dikonversi kembali ke skala harga agar dapat dibandingkan dengan harga aktual.

HASIL FORECAST

hasil_forecast <- data.frame(
  
  Date = test$Date,
  
  Actual = test$Close,
  
  Forecast = harga_forecast,
  
  Return_Forecast = return_forecast,
  
  Volatility = vol_forecast
)

head(hasil_forecast)
##         Date Actual Forecast Return_Forecast Volatility
## 1 2025-11-06 269.77 270.3401    0.0007404735 0.01199545
## 2 2025-11-07 268.47 270.5404    0.0007404735 0.01210026
## 3 2025-11-10 269.43 270.7408    0.0007404735 0.01220280
## 4 2025-11-11 275.25 270.9413    0.0007404735 0.01230310
## 5 2025-11-12 273.47 271.1420    0.0007404735 0.01240118
## 6 2025-11-13 272.95 271.3429    0.0007404735 0.01249706
tail(hasil_forecast)
##           Date Actual Forecast Return_Forecast Volatility
## 220 2026-09-23 337.02 317.9342    0.0007404735 0.01602547
## 221 2026-09-24 335.92 318.1697    0.0007404735 0.01602565
## 222 2026-09-25 341.07 318.4054    0.0007404735 0.01602582
## 223 2026-09-28 338.40 318.6413    0.0007404735 0.01602599
## 224 2026-09-29 329.40 318.8773    0.0007404735 0.01602615
## 225 2026-09-30 333.02 319.1135    0.0007404735 0.01602630

Tabel ini memungkinkan perbandingan langsung antara hasil prediksi dan kondisi aktual.

Contoh observasi pertama:

Aktual = 269,77 Forecast = 270,3401

Artinya forecast pada observasi pertama berada cukup dekat dengan harga aktual.

Namun pada akhir periode:

Aktual = 333,02 Forecast = 319,1135.

Terlihat bahwa semakin panjang horizon, perbedaan antara forecast dan aktual dapat semakin besar.

PLOT HARGA AKTUAL VS FORECAST

hasil_plot <- hasil_forecast %>%
  select(
    Date,
    Actual,
    Forecast
  ) %>%
  pivot_longer(
    cols = c(
      Actual,
      Forecast
    ),
    names_to = "Jenis",
    values_to = "Harga"
  )


ggplot(
  hasil_plot,
  aes(
    x = Date,
    y = Harga,
    linetype = Jenis
  )
) +
  geom_line(linewidth = 1) +
  labs(
    title = "Perbandingan Harga Aktual dan Hasil Peramalan Saham APPLE INC.",
    x = "Tanggal",
    y = "Harga Saham (Rp)",
    linetype = ""
  ) +
  theme_minimal()

Grafik memberikan evaluasi visual mengenai kemampuan model mengikuti perubahan harga aktual.

Apabila garis forecast dekat dengan garis aktual, berarti error relatif kecil.

Sebaliknya, jarak yang semakin besar menunjukkan kesalahan prediksi yang semakin besar.

Model Evaluation

MAE <- mean(
  abs(
    hasil_forecast$Actual -
      hasil_forecast$Forecast
  ),
  na.rm = TRUE
)

MAE
## [1] 14.73405
RMSE <- sqrt(
  mean(
    (
      hasil_forecast$Actual -
        hasil_forecast$Forecast
    )^2,
    na.rm = TRUE
  )
)

RMSE
## [1] 18.6248
MAPE <- mean(
  abs(
    (
      hasil_forecast$Actual -
        hasil_forecast$Forecast
    ) /
      hasil_forecast$Actual
  ),
  na.rm = TRUE
) * 100

MAPE
## [1] 5.339094
SMAPE <- mean(
  2 *
    abs(
      hasil_forecast$Actual -
        hasil_forecast$Forecast
    ) /
    (
      abs(hasil_forecast$Actual) +
        abs(hasil_forecast$Forecast)
    ),
  na.rm = TRUE
) * 100

SMAPE
## [1] 5.170464
evaluasi <- data.frame(Model = "EGARCH(1,1)",MAE = MAE,RMSE = RMSE,MAPE = MAPE,SMAPE = SMAPE
)
evaluasi
##         Model      MAE    RMSE     MAPE    SMAPE
## 1 EGARCH(1,1) 14.73405 18.6248 5.339094 5.170464

MAE menunjukkan rata-rata nilai absolut kesalahan prediksi. Secara sederhana, hasil ini berarti: rata-rata selisih absolut antara harga aktual dan harga forecast sekitar 14,73 USD. MAE semakin kecil → kesalahan rata-rata semakin kecil.

RMSE memberikan bobot lebih besar terhadap kesalahan yang besar karena error dikuadratkan. Nilai 18,62 menunjukkan besarnya kesalahan prediksi setelah memperhitungkan error yang besar. Karena: \[ RMSE > MAE \] terdapat indikasi bahwa terdapat beberapa error yang relatif besar.

MAPE menunjukkan rata-rata persentase absolut kesalahan prediksi terhadap harga aktual. Hasilnya sekitar 5,34%. Artinya, secara rata-rata, kesalahan relatif forecast sekitar 5,34% dari harga aktual.

SMAPE merupakan ukuran error persentase yang menggunakan rata-rata nilai absolut aktual dan forecast sebagai penyebut. Nilai 5,17% menunjukkan tingkat kesalahan relatif yang diperoleh model berdasarkan formula SMAPE yang digunakan dalam penelitian.

Kesimpulan

Secara khusus, data return sudah stasioner dan residual model mean memiliki efek ARCH yang signifikan. GARCH kemudian menunjukkan adanya indikasi negative sign bias, sehingga model EGARCH digunakan untuk mengakomodasi kemungkinan asimetri tersebut.

EGARCH memiliki AIC dan BIC yang lebih rendah dibandingkan GARCH dalam hasil penelitian ini. Selain itu, diagnostik residual EGARCH menunjukkan tidak adanya autokorelasi residual yang signifikan, tidak adanya ARCH effect yang signifikan, dan tidak adanya sign bias yang signifikan.

Peramalan n-periode ke depan

# 1. Melakukan peramalan 12 langkah ke depan dari tanggal terakhir (30 Sep 2026)
forecast_12_ahead <- ugarchforecast(
  fit_egarch,
  n.ahead = 12
)

# 2. Mengambil hasil forecast return dan volatilitas (sigma)
return_12 <- as.numeric(fitted(forecast_12_ahead))
vol_12    <- as.numeric(sigma(forecast_12_ahead))

# 3. Mengambil harga penutupan terakhir dari seluruh dataset (30 September 2026)
harga_terakhir_total <- tail(data_AAPL$Close, 1)

# 4. Mengonversi log return menjadi perkiraan harga saham AAPL (USD)
harga_12 <- harga_terakhir_total * exp(cumsum(return_12))

# 5. Membuat estimasi tanggal perdagangan 12 hari ke depan (mengabaikan akhir pekan)
tanggal_terakhir <- max(data_AAPL$Date)
tanggal_12 <- seq(from = tanggal_terakhir + 1, by = "day", length.out = 20)
tanggal_12 <- tanggal_12[!weekdays(tanggal_12) %in% c("Saturday", "Sunday")][1:12]

# 6. Menyusun hasil peramalan ke dalam data frame
tabel_forecast_12 <- data.frame(
  Periode         = 1:12,
  Tanggal         = tanggal_12,
  Forecast_Return = return_12,
  Forecast_Volatilitas = vol_12,
  Forecast_Harga_USD  = harga_12
)

# Menampilkan tabel hasil peramalan
print("=== HASIL PERAMALAN 12 PERIODE KE DEPAN SAHAM AAPL ===")
## [1] "=== HASIL PERAMALAN 12 PERIODE KE DEPAN SAHAM AAPL ==="
print(tabel_forecast_12)
##    Periode    Tanggal Forecast_Return Forecast_Volatilitas Forecast_Harga_USD
## 1        1 2026-10-01    0.0007404735           0.01199545           333.2667
## 2        2 2026-10-02    0.0007404735           0.01210026           333.5135
## 3        3 2026-10-05    0.0007404735           0.01220280           333.7606
## 4        4 2026-10-06    0.0007404735           0.01230310           334.0078
## 5        5 2026-10-07    0.0007404735           0.01240118           334.2552
## 6        6 2026-10-08    0.0007404735           0.01249706           334.5028
## 7        7 2026-10-09    0.0007404735           0.01259077           334.7506
## 8        8 2026-10-12    0.0007404735           0.01268235           334.9986
## 9        9 2026-10-13    0.0007404735           0.01277181           335.2467
## 10      10 2026-10-14    0.0007404735           0.01285919           335.4951
## 11      11 2026-10-15    0.0007404735           0.01294453           335.7436
## 12      12 2026-10-16    0.0007404735           0.01302784           335.9923

Forecast return untuk 12 periode bernilai konstan:

\[ 0,0007404735 \]

Hal ini konsisten dengan model mean ARIMA(0,0,0).

Sementara itu, volatilitas diproyeksikan meningkat dari:

\[ 0,011995 \]

menjadi:

\[ 0,013028. \]

Jadi, model memperkirakan return ekspektasian yang relatif konstan, tetapi volatilitas meningkat secara bertahap selama horizon 12 hari.

Harga hasil forecast meningkat dari:

\[ 333,2667 \]

menjadi:

\[ 335,9923. \]

Kenaikan tersebut merupakan konsekuensi dari return forecast positif yang diaplikasikan secara kumulatif ke harga terakhir sebesar 333,02 USD. ### Visualisasi

ggplot(tabel_forecast_12, aes(x = Tanggal, y = Forecast_Harga_USD)) +
  geom_line(color = "blue", linewidth = 1) +
  geom_point(color = "red", size = 2) +
  scale_x_date(date_labels = "%d %b %Y", date_breaks = "2 days") +
  labs(
    title = "Proyeksi Pergerakan Harga Saham AAPL (12 Periode Ke Depan)",
    subtitle = "Diestimasi Menggunakan Model EGARCH(1,1)",
    x = "Tanggal Perdagangan",
    y = "Perkiraan Harga Saham (USD)"
  ) +
  theme_minimal()

Kesimpulan: Pertama, data harga saham AAPL dikumpulkan untuk periode April 2022 sampai September 2026. Harga penutupan kemudian digunakan untuk membentuk log return. Kedua, return diuji menggunakan ADF dan diperoleh hasil stasioner. Ketiga, model mean ditentukan menggunakan Auto ARIMA dan diperoleh ARIMA(0,0,0) dengan zero mean. Keempat, residual model mean diuji menggunakan ARCH-LM dan ditemukan adanya efek ARCH yang signifikan, sehingga pemodelan volatilitas menggunakan keluarga GARCH diperlukan. Kelima, model GARCH(1,1) dibangun dan ditemukan indikasi Negative Sign Bias. Oleh karena itu, digunakan EGARCH(1,1) yang dapat mengakomodasi asimetri volatilitas. Keenam, hasil EGARCH menunjukkan AIC dan BIC yang lebih rendah dibandingkan GARCH. Diagnostik residual EGARCH juga menunjukkan tidak adanya autokorelasi residual dan tidak adanya ARCH effect yang signifikan. Ketujuh, model EGARCH digunakan untuk melakukan forecasting pada data testing dan menghasilkan MAE 14,73405, RMSE 18,6248, MAPE 5,3391%, dan SMAPE 5,1705%. Terakhir, forecasting 12 periode ke depan menghasilkan proyeksi harga dari 333,27 USD hingga 335,99 USD dengan volatilitas yang meningkat dari sekitar 0,0120 menjadi 0,0130.