Proyek Ekonometrika - VECM

A. Library

library(readxl)
library(lubridate)
library(dplyr)
library(zoo)
library(psych)
library(patchwork)
library(ggplot2)
library(urca)
library(vars)

B. Data

2.1. Import Data

data <- read_xlsx("D:/UNY/MySta/SEM 5/Ekonometrika/Proyek/Dataset_Ekonom.xlsx")
head(data)
## # A tibble: 6 × 6
##   Date          Inflasi BI_rate  Kurs Money_supply  IHSG
##   <chr>           <dbl>   <dbl> <dbl>        <dbl> <dbl>
## 1 Januari 2013     4.57    5.75   NA      3268789. 4454.
## 2 Februari 2013    5.31    5.75   NA      3280420. 4796.
## 3 Maret 2013       5.9     5.75   NA      3322529. 4941.
## 4 April 2013       5.57    5.75   NA      3360928. 5034.
## 5 Mei 2013         5.47    5.75 9786.     3426305. 5069.
## 6 Juni 2013        5.9     6    9882.     3413379. 4819.

2.2. Data Structure, Character Variables, and Size

# Structure
str(data)
## tibble [144 × 6] (S3: tbl_df/tbl/data.frame)
##  $ Date        : chr [1:144] "Januari 2013" "Februari 2013" "Maret 2013" "April 2013" ...
##  $ Inflasi     : num [1:144] 4.57 5.31 5.9 5.57 5.47 5.9 8.61 8.79 8.4 8.32 ...
##  $ BI_rate     : num [1:144] 5.75 5.75 5.75 5.75 5.75 6 6.5 7 7.25 7.25 ...
##  $ Kurs        : num [1:144] NA NA NA NA 9786 ...
##  $ Money_supply: num [1:144] 3268789 3280420 3322529 3360928 3426305 ...
##  $ IHSG        : num [1:144] 4454 4796 4941 5034 5069 ...
# Character variable
sapply(data, class)
##         Date      Inflasi      BI_rate         Kurs Money_supply         IHSG 
##  "character"    "numeric"    "numeric"    "numeric"    "numeric"    "numeric"
# Size
cat("Number of rows:", nrow(data), "\n")
## Number of rows: 144
cat("Number of columns:", ncol(data), "\n")
## Number of columns: 6

2.3. Data Summary

summary(data)
##      Date              Inflasi         BI_rate           Kurs      
##  Length:144         Min.   :1.320   Min.   :3.500   Min.   : 9786  
##  Class :character   1st Qu.:2.663   1st Qu.:4.438   1st Qu.:13313  
##  Mode  :character   Median :3.340   Median :5.750   Median :14114  
##                     Mean   :3.927   Mean   :5.543   Mean   :13920  
##                     3rd Qu.:4.947   3rd Qu.:6.500   3rd Qu.:14831  
##                     Max.   :8.790   Max.   :7.750   Max.   :16337  
##                                                     NA's   :4      
##   Money_supply          IHSG     
##  Min.   :3268789   Min.   :4195  
##  1st Qu.:4518614   1st Qu.:5082  
##  Median :5670877   Median :5944  
##  Mean   :6002823   Mean   :5853  
##  3rd Qu.:7657626   3rd Qu.:6612  
##  Max.   :9210816   Max.   :7671  
## 

C. Data Preprocessing

3.1. Konversi kolom Date

bulan_id <- c("Januari"="01","Februari"="02","Maret"="03","April"="04",
              "Mei"="05","Juni"="06","Juli"="07","Agustus"="08",
              "September"="09","Oktober"="10","November"="11","Desember"="12")

data <- data %>%
  mutate(
    Month = sapply(strsplit(Date, " "), `[`, 1),
    Year  = sapply(strsplit(Date, " "), `[`, 2),
    Month_num = bulan_id[Month],
    Date2 = ymd(paste(Year, Month_num, "01", sep="-"))
  ) %>%
  dplyr::select(Date2, Inflasi, BI_rate, Kurs, Money_supply, IHSG)

# short by time
data <- data %>% arrange(Date2)

3.2. Missing and Duplicate Values

# Check missing values
colSums(is.na(data))
##        Date2      Inflasi      BI_rate         Kurs Money_supply         IHSG 
##            0            0            0            4            0            0
# Check duplicate values
sum(duplicated(data))
## [1] 0

Handling Missing Value: dengan na.fill(“extend”) karena metode ini stabil untuk time-series ekonomi, dengan mengisi NA awal menggunakan nilai valid terdekat tanpa menciptakan pola data yang tidak nyata.

# Handling missing value
data$Kurs <- na.fill(data$Kurs, "extend")
# Cek NA kembali
colSums(is.na(data))
##        Date2      Inflasi      BI_rate         Kurs Money_supply         IHSG 
##            0            0            0            0            0            0

3.3. Deteksi Outlier

# Outlier tiap variabel (IQR method)
vars <- c("Inflasi", "BI_rate", "Kurs", "Money_supply", "IHSG")

outlier_count <- sapply(vars, function(v){
  x <- data[[v]]
  Q1 <- quantile(x, 0.25, na.rm = TRUE)
  Q3 <- quantile(x, 0.75, na.rm = TRUE)
  IQR_val <- Q3 - Q1
  sum(x < (Q1 - 1.5*IQR_val) | x > (Q3 + 1.5*IQR_val), na.rm = TRUE)
})

# Tabel distribusi outlier
outlier_table <- data.frame(Variabel = vars, Jumlah_Outlier = outlier_count)
print(outlier_table)
##                  Variabel Jumlah_Outlier
## Inflasi           Inflasi              4
## BI_rate           BI_rate              0
## Kurs                 Kurs              8
## Money_supply Money_supply              0
## IHSG                 IHSG              0
# Boxplot
par(mfrow=c(2,3))
boxplot(data$Inflasi, main="Inflasi")
boxplot(data$BI_rate, main="BI Rate")
boxplot(data$Kurs, main="Kurs")
boxplot(data$Money_supply, main="Money Supply")
boxplot(data$IHSG, main="IHSG")

Interpretasi distribusi outlier: Outlier tidak perlu di-handling karena seluruh nilai ekstrem tersebut merupakan shock ekonomi yang nyata—bukan kesalahan data, sehingga dipertahankan agar dinamika hubungan antarvariabel dalam model VECM tetap mencerminkan kondisi ekonomi sebenarnya.

3.4. Transformasi log

Pada time-series ekonomi, variabel Kurs, Money Supply, dan IHSG dilog karena ketiganya bersifat trending dan berskala besar, sehingga log transform membuat varians lebih stabil, hubungan lebih linear, dan hasil VECM lebih valid serta ekonomis untuk diinterpretasikan

data <- data %>%
  mutate(
    ln_Kurs  = log(Kurs),
    ln_M2    = log(Money_supply),
    ln_IHSG  = log(IHSG)
  )
# Dataset Final setelah Preprocessing
final_data <- data %>%
  dplyr::select(Date2, Inflasi, BI_rate, ln_Kurs, ln_M2, ln_IHSG)

D. Exploratory Data Analysis

4.1. Deskripsi Statistik

describe(final_data[, -1])
##         vars   n  mean   sd median trimmed  mad   min   max range  skew
## Inflasi    1 144  3.93 1.94   3.34    3.74 1.62  1.32  8.79  7.47  0.83
## BI_rate    2 144  5.54 1.34   5.75    5.55 1.48  3.50  7.75  4.25  0.03
## ln_Kurs    3 144  9.53 0.11   9.55    9.54 0.08  9.19  9.70  0.51 -1.23
## ln_M2      4 144 15.56 0.30  15.55   15.57 0.37 15.00 16.04  1.04 -0.10
## ln_IHSG    5 144  8.66 0.16   8.69    8.67 0.21  8.34  8.95  0.60 -0.18
##         kurtosis   se
## Inflasi    -0.20 0.16
## BI_rate    -1.15 0.11
## ln_Kurs     1.46 0.01
## ln_M2      -1.15 0.03
## ln_IHSG    -1.14 0.01

4.2. Plot Time Series untuk Setiap Variabel

# line plot time-series
plot_ts <- function(df, var, label){
ggplot(df, aes(x = Date2, y = .data[[var]])) +
geom_line(color = "steelblue", linewidth = 1) +
labs(title = paste("", label),
x = "Tahun",
y = label) +
theme_minimal()
}

p1 <- plot_ts(final_data, "Inflasi", "Inflasi (%)")
p2 <- plot_ts(final_data, "BI_rate", "BI Rate (%)")
p3 <- plot_ts(final_data, "ln_Kurs", "Kurs (Log)")
p4 <- plot_ts(final_data, "ln_M2", "Money Supply (Log)")
p5 <- plot_ts(final_data, "ln_IHSG", "IHSG (Log)")

(p1 + p2 + p3) /
(p4 + p5 + plot_spacer())

4.3. Plot IHSG per Tahun

final_data$Year  <- format(final_data$Date2, "%Y")
final_data$Bulan <- format(final_data$Date2, "%b")
final_data$Bulan <- factor(
  final_data$Bulan,
  levels = c("Jan","Feb","Mar","Apr","May","Jun",
             "Jul","Aug","Sep","Oct","Nov","Dec")
)

ggplot(final_data, aes(x = Bulan, y = ln_IHSG, color = Year, group = Year)) +
  geom_line(linewidth = 1) +
  labs(
    title = "Pergerakan IHSG Berdasarkan Tahun",
    x = "Bulan",
    y = "ln(IHSG)",
    color = "Tahun"
  ) +
  theme_minimal()

E. Hasil Analisis

1. Pengujian Stasioneritas

uji stasioneritas data dalam penelitian ini adalah uji Augmented Dickey Fuller dengan menggunakan taraf signifikan 5%. Pengujian hasil stasioneritas pada tingkat level dan first difference.

df <- final_data %>%
  dplyr::select(
    Y = ln_IHSG,
    X1 = ln_Kurs,
    X2 = ln_M2,
    X3 = Inflasi,
    X4 = BI_rate
  )
crit_5 <- -2.89

adf_test <- function(x){
  # LEVEL
  test_level <- ur.df(x, type="drift", selectlags="AIC")
  adf_level  <- test_level@teststat["statistic", "tau2"]

  # FIRST DIFF
  test_diff  <- ur.df(diff(x), type="drift", selectlags="AIC")
  adf_diff   <- test_diff@teststat["statistic", "tau2"]

  return(c(ADF_Level = adf_level,
           ADF_FirstDiff = adf_diff))
}

adf_results <- t(sapply(df, adf_test))

ADF_Table <- data.frame(
  Variabel = rownames(adf_results),
  ADF_Level = round(adf_results[,1], 4),
  Ket_Level = ifelse(adf_results[,1] < crit_5, "Stasioner", "Tidak Stasioner"),
  ADF_FirstDiff = round(adf_results[,2], 4),
  Ket_Diff = ifelse(adf_results[,2] < crit_5, "Stasioner", "Tidak Stasioner")
)

print(ADF_Table)
##    Variabel ADF_Level       Ket_Level ADF_FirstDiff  Ket_Diff
## Y         Y   -1.5345 Tidak Stasioner       -7.5032 Stasioner
## X1       X1   -2.8051 Tidak Stasioner      -10.0603 Stasioner
## X2       X2   -1.7139 Tidak Stasioner      -10.7190 Stasioner
## X3       X3   -2.0059 Tidak Stasioner       -8.6798 Stasioner
## X4       X4   -1.4064 Tidak Stasioner       -5.0179 Stasioner

Interpretasi: variabel Y, X1, X2, X3, X4 merupakan data yang tidak stasioner pada tingkat level (data asli) karena nilai ADF lebih besar dibandingkan dengan tabel MacKinnon dengan tingkat signifikansi 5%. Sementara itu, dari hasil diferensi pertama terlihat bahwa nilai ADF lebih kecil dari tingkat signifikansi artinya semua variabel telah stasioner.

2. Pengujian Lag Optimal

Tahap ini menentukan panjang lag optimal pada penelitian ini didasarkan pada Akaike Information Criterion (AIC)

lag_sel <- VARselect(df, lag.max = 10, type = "const")

# Nilai AIC dari semua lag
AIC_values <- lag_sel$criteria["AIC(n)", ]

# Tabel
Tabel_Lag <- data.frame(
  Lag = 1:length(AIC_values),
  AIC = round(AIC_values, 5)
)
print(Tabel_Lag)
##    Lag       AIC
## 1    1 -28.43777
## 2    2 -28.79574
## 3    3 -28.75053
## 4    4 -28.51073
## 5    5 -28.42499
## 6    6 -28.33043
## 7    7 -28.36593
## 8    8 -28.28466
## 9    9 -28.17697
## 10  10 -28.16016
# Lag optimal
lag_optimal <- which.min(AIC_values)
AIC_min <- AIC_values[lag_optimal]
cat("Lag optimal =", lag_optimal, "\n")
## Lag optimal = 2
cat("Nilai AIC terkecil =", round(AIC_min, 5), "\n")
## Nilai AIC terkecil = -28.79574

Interpretasi: panjang lag optimal terletak pada lag 2 dikarenakan memiliki nilai AIC terkecil yaitu sebesar -28.79574. Penggunaan lag 2 sebagai lag optimal menyatakan bahwa semua variabel saling mempengaruhi satu sama lain tidak hanya pada periode yang sama, namun variabel-variabel tersebut saling berkaitan hingga pada dua periode sebelumnya.

3. Pengujian Stabilitas Model VAR

# lag optimal = 2
lag_optimal <- which.min(VARselect(df, lag.max=10, type="const")$criteria["AIC(n)", ])

# Estimasi VAR sesuai lag optimal
var_model <- VAR(df, p = lag_optimal, type = "const")

# Uji stabilitas (roots)
VAR_roots <- roots(var_model)

# Tabel modulus & roots
Stabilitas_VAR <- data.frame(
  Root = VAR_roots,
  Modulus = Mod(VAR_roots)
)

print(Stabilitas_VAR)
##          Root    Modulus
## 1  0.99661107 0.99661107
## 2  0.93111825 0.93111825
## 3  0.91246959 0.91246959
## 4  0.91246959 0.91246959
## 5  0.78482872 0.78482872
## 6  0.40582520 0.40582520
## 7  0.29049035 0.29049035
## 8  0.29049035 0.29049035
## 9  0.25863738 0.25863738
## 10 0.03495285 0.03495285

Interpretasi: Berdasarkan hasil uji stabilitas model VAR, seluruh nilai roots memiliki modulus kurang dari satu dan seluruh titik roots berada di dalam lingkaran satuan.Hal ini menunjukkan bahwa model VAR yang digunakan adalah stabil (stationary) sehingga analisis lebih lanjut seperti pengujian kointegrasi dan estimasi VECM dapat dilakukan.

4. Pengujian Kointegrasi

# Uji Johansen (menggunakan lag optimal - 1)
# CATATAN: Johansen memakai "K = p - 1"
johansen_test <- ca.jo(df, type = "trace", K = lag_optimal, ecdet = "const", spec = "transitory")

summary(johansen_test)
## 
## ###################### 
## # Johansen-Procedure # 
## ###################### 
## 
## Test type: trace statistic , without linear trend and constant in cointegration 
## 
## Eigenvalues (lambda):
## [1]  3.986501e-01  1.976767e-01  1.336932e-01  4.147073e-02  3.117743e-02
## [6] -2.131724e-15
## 
## Values of teststatistic and critical values of test:
## 
##            test 10pct  5pct  1pct
## r <= 4 |   4.50  7.52  9.24 12.97
## r <= 3 |  10.51 17.85 19.96 24.60
## r <= 2 |  30.89 32.00 34.91 41.07
## r <= 1 |  62.17 49.65 53.12 60.16
## r = 0  | 134.38 71.86 76.07 84.45
## 
## Eigenvectors, normalised to first column:
## (These are the cointegration relations)
## 
##                  Y.l1        X1.l1       X2.l1        X3.l1       X4.l1
## Y.l1       1.00000000  1.000000000   1.0000000  1.000000000  1.00000000
## X1.l1      1.63639940 -2.347329662   0.6785861  1.451395139  0.37106210
## X2.l1     -0.25408683  0.150774382   2.6222302 -0.896834967 -0.51005230
## X3.l1      0.12714169 -0.017583699   1.0794176  0.004866563 -0.02954883
## X4.l1     -0.04752117 -0.007792258  -0.5421837 -0.042691553  0.15911267
## constant -21.03194520 11.455866386 -56.7590799 -8.296409544 -5.01250602
##             constant
## Y.l1      1.00000000
## X1.l1     5.08678227
## X2.l1    -4.14774949
## X3.l1     0.08996298
## X4.l1    -0.29174454
## constant  8.61633394
## 
## Weights W:
## (This is the loading matrix)
## 
##              Y.l1        X1.l1         X2.l1        X3.l1        X4.l1
## Y.d  -0.005641709 -0.047165455 -0.0018290061 -0.034895086 -0.023780677
## X1.d -0.006601895  0.037611610  0.0019178900 -0.014428688  0.005944866
## X2.d -0.019756641  0.003346803  0.0009508489  0.004595116  0.001376368
## X3.d -0.079672001  0.973766075 -0.0967623145  0.306905864 -0.111373763
## X4.d  0.044628002  0.187454736  0.0285679444  0.144510574 -0.077889761
##           constant
## Y.d  -2.365637e-14
## X1.d  6.031912e-14
## X2.d  1.408469e-14
## X3.d  7.102390e-13
## X4.d  1.705686e-13
# max eigen value
johansen_test_max <- ca.jo(df, type = "eigen", K = lag_optimal, ecdet = "const", spec = "transitory")

summary(johansen_test_max)
## 
## ###################### 
## # Johansen-Procedure # 
## ###################### 
## 
## Test type: maximal eigenvalue statistic (lambda max) , without linear trend and constant in cointegration 
## 
## Eigenvalues (lambda):
## [1]  3.986501e-01  1.976767e-01  1.336932e-01  4.147073e-02  3.117743e-02
## [6] -2.131724e-15
## 
## Values of teststatistic and critical values of test:
## 
##           test 10pct  5pct  1pct
## r <= 4 |  4.50  7.52  9.24 12.97
## r <= 3 |  6.01 13.75 15.67 20.20
## r <= 2 | 20.38 19.77 22.00 26.81
## r <= 1 | 31.27 25.56 28.14 33.24
## r = 0  | 72.22 31.66 34.40 39.79
## 
## Eigenvectors, normalised to first column:
## (These are the cointegration relations)
## 
##                  Y.l1        X1.l1       X2.l1        X3.l1       X4.l1
## Y.l1       1.00000000  1.000000000   1.0000000  1.000000000  1.00000000
## X1.l1      1.63639940 -2.347329662   0.6785861  1.451395139  0.37106210
## X2.l1     -0.25408683  0.150774382   2.6222302 -0.896834967 -0.51005230
## X3.l1      0.12714169 -0.017583699   1.0794176  0.004866563 -0.02954883
## X4.l1     -0.04752117 -0.007792258  -0.5421837 -0.042691553  0.15911267
## constant -21.03194520 11.455866386 -56.7590799 -8.296409544 -5.01250602
##             constant
## Y.l1      1.00000000
## X1.l1     5.08678227
## X2.l1    -4.14774949
## X3.l1     0.08996298
## X4.l1    -0.29174454
## constant  8.61633394
## 
## Weights W:
## (This is the loading matrix)
## 
##              Y.l1        X1.l1         X2.l1        X3.l1        X4.l1
## Y.d  -0.005641709 -0.047165455 -0.0018290061 -0.034895086 -0.023780677
## X1.d -0.006601895  0.037611610  0.0019178900 -0.014428688  0.005944866
## X2.d -0.019756641  0.003346803  0.0009508489  0.004595116  0.001376368
## X3.d -0.079672001  0.973766075 -0.0967623145  0.306905864 -0.111373763
## X4.d  0.044628002  0.187454736  0.0285679444  0.144510574 -0.077889761
##           constant
## Y.d  -2.365637e-14
## X1.d  6.031912e-14
## X2.d  1.408469e-14
## X3.d  7.102390e-13
## X4.d  1.705686e-13

5. Estimasi Vector Error Correction Model

6. Pengujian Asumsi Klasik

a. Multikolinearitas

b. Autokorelasi

c. Homoskedastisitas

d. Normalitas

7. Peramalan