Tentang Proyek Portofolio Ini

Dokumen ini merupakan bagian dari rangkaian analisis peramalan kurs USD/IDR periode 2021–2025 menggunakan pemodelan stokastik Geometric Brownian Motion (GBM) dan Simulasi Monte Carlo. Proyek ini bertujuan untuk mengestimasi lintasan nilai tukar harian ke depan berdasarkan volatilitas dan drift historis.

Struktur Publikasi:

  1. [Data Wrangling] Persiapan & Transformasi Data
  2. [Statistik Deskriptif] Eksplorasi Deret Waktu
  3. [Uji Normalitas] Pengujian Asumsi Distribusi Log-Return
  4. [Simulasi] Estimasi Parameter & Monte Carlo
  5. [Analisis] Interval Kepercayaan & Proyeksi Final

🔗 Tautan Terkait: GitHub Repository | Dashboard Interaktif R Shiny

1 Latar Belakang

Simulasi Monte Carlo menghasilkan ribuan lintasan harga yang mungkin terjadi berdasarkan model GBM. Dari kumpulan lintasan tersebut, kita dapat mengekstrak:

  1. Ekspektasi — nilai rata-rata kurs yang diproyeksikan
  2. Ketidakpastian — rentang kemungkinan melalui interval kepercayaan
  3. Stabilitas — seberapa konsisten hasil antar jumlah iterasi yang berbeda

2 Persiapan

2.1 Memuat Paket

library(dplyr)
library(ggplot2)
library(tidyr)
library(knitr)
library(kableExtra)

set.seed(42)

2.2 Memuat Data & Hasil Simulasi

# Baca data historis
df_raw <- read.csv(
"C:/Users/anjan/Documents/Downloads/usd-idr-portfolio-github/usd-idr-gbm-montecarlo/data/Data Historis USD_IDR.csv",
  header = TRUE, stringsAsFactors = FALSE, fileEncoding = "UTF-8-BOM"
)

df_usd_idr <- df_raw %>%
  select(Tanggal, Terakhir) %>%
  mutate(
    Kurs    = as.numeric(gsub(",", ".", gsub("\\.", "", Terakhir))),
    Tanggal = as.Date(Tanggal, format = "%d/%m/%Y")
  ) %>%
  select(Tanggal, Kurs) %>%
  arrange(Tanggal)

# Muat hasil simulasi dari Bagian 6
hasil <- readRDS("hasil_simulasi.rds")

sim_1000  <- hasil$sim_1000
sim_5000  <- hasil$sim_5000
sim_10000 <- hasil$sim_10000
S0        <- hasil$S0
mu        <- hasil$mu
sigma     <- hasil$sigma
horizon   <- hasil$horizon
T0        <- hasil$tanggal_S0

cat("Hasil simulasi berhasil dimuat.\n")
## Hasil simulasi berhasil dimuat.
cat(sprintf("S0      : Rp %s\n", formatC(S0, format="f", digits=0, big.mark=".")))
## S0      : Rp 16.675
cat(sprintf("µ       : %.8f\n", mu))
## µ       : 0.00012605
cat(sprintf("σ       : %.8f\n", sigma))
## σ       : 0.00321561
cat(sprintf("Horizon : %d hari\n", horizon))
## Horizon : 252 hari
cat(sprintf("Sim 1K  : %d × %d\n", nrow(sim_1000),  ncol(sim_1000)))
## Sim 1K  : 253 × 1000
cat(sprintf("Sim 5K  : %d × %d\n", nrow(sim_5000),  ncol(sim_5000)))
## Sim 5K  : 253 × 5000
cat(sprintf("Sim 10K : %d × %d\n", nrow(sim_10000), ncol(sim_10000)))
## Sim 10K : 253 × 10000

2.3 Membuat Sumbu Waktu

# Buat vektor tanggal untuk sumbu x simulasi
# Mulai dari T0+1 hari kerja, sebanyak horizon hari
tanggal_sim <- seq(T0 + 1, by = "day", length.out = horizon * 2)
tanggal_sim <- tanggal_sim[!weekdays(tanggal_sim) %in% c("Saturday","Sunday")]
tanggal_sim <- c(T0, tanggal_sim[1:horizon])

cat("Rentang simulasi:", format(min(tanggal_sim), "%d %B %Y"),
    "–", format(max(tanggal_sim), "%d %B %Y"), "\n")
## Rentang simulasi: 31 December 2025 – 18 December 2026
cat("Jumlah titik waktu:", length(tanggal_sim), "\n")
## Jumlah titik waktu: 253

3 Tahap 1 — Fan Chart per Skenario

Fan chart menampilkan seluruh lintasan simulasi sekaligus, memberikan gambaran visual tentang rentang kemungkinan pergerakan kurs.

3.1 Helper: Fungsi Pembuatan Fan Chart

buat_fan_chart <- function(sim_matrix, tanggal, S0_kurs, n_sim_label,
                            warna_jalur = "#3b82f6", warna_median = "#1e40af",
                            warna_band  = "#93c5fd") {

  n_time <- nrow(sim_matrix)
  n_sim  <- ncol(sim_matrix)

  # Hitung statistik per waktu
  df_stats <- data.frame(
    Tanggal = tanggal,
    median  = apply(sim_matrix, 1, median),
    p05     = apply(sim_matrix, 1, quantile, 0.05),
    p25     = apply(sim_matrix, 1, quantile, 0.25),
    p75     = apply(sim_matrix, 1, quantile, 0.75),
    p95     = apply(sim_matrix, 1, quantile, 0.95),
    mean    = apply(sim_matrix, 1, mean)
  )

  # Ambil sampel lintasan untuk ditampilkan (max 300 agar tidak berat)
  n_sample <- min(300, n_sim)
  idx      <- sample(1:n_sim, n_sample)

  df_paths <- as.data.frame(sim_matrix[, idx])
  df_paths$Tanggal <- tanggal
  df_long <- df_paths %>%
    pivot_longer(-Tanggal, names_to = "sim", values_to = "Kurs")

  # Data historis 6 bulan terakhir untuk konteks
  df_hist <- df_usd_idr %>%
    filter(Tanggal >= S0_kurs - 180)

  ggplot() +
    # Lintasan individual (transparan)
    geom_line(data = df_long,
              aes(x = Tanggal, y = Kurs, group = sim),
              color = warna_jalur, alpha = 0.04, linewidth = 0.3) +
    # Band 90% (P5–P95)
    geom_ribbon(data = df_stats,
                aes(x = Tanggal, ymin = p05, ymax = p95),
                fill = warna_band, alpha = 0.2) +
    # Band 50% (P25–P75)
    geom_ribbon(data = df_stats,
                aes(x = Tanggal, ymin = p25, ymax = p75),
                fill = warna_band, alpha = 0.3) +
    # Garis median
    geom_line(data = df_stats,
              aes(x = Tanggal, y = median),
              color = warna_median, linewidth = 1.0, linetype = "solid") +
    # Garis mean
    geom_line(data = df_stats,
              aes(x = Tanggal, y = mean),
              color = "#ef4444", linewidth = 0.8, linetype = "dashed", alpha = 0.8) +
    # Data historis
    geom_line(data = df_hist,
              aes(x = Tanggal, y = Kurs),
              color = "#1e293b", linewidth = 0.9, alpha = 0.9) +
    # Garis pemisah historis vs simulasi
    geom_vline(xintercept = as.numeric(tanggal[1]),
               color = "#64748b", linewidth = 0.6, linetype = "dotted") +
    # Titik S0
    geom_point(aes(x = tanggal[1], y = sim_matrix[1,1]),
               color = "#1e293b", size = 3, shape = 21,
               fill = "white", stroke = 1.5) +
    scale_y_continuous(
      labels = function(x) formatC(x, format="f", digits=0, big.mark=".")
    ) +
    scale_x_date(date_breaks = "2 months", date_labels = "%b '%y") +
    labs(
      subtitle = sprintf(
        "%s iterasi  |  Median: Rp %s  |  CI 90%%: Rp %s – Rp %s",
        n_sim_label,
        formatC(tail(df_stats$median, 1), format="f", digits=0, big.mark="."),
        formatC(tail(df_stats$p05,    1), format="f", digits=0, big.mark="."),
        formatC(tail(df_stats$p95,    1), format="f", digits=0, big.mark=".")
      ),
      x = NULL, y = "Kurs USD/IDR (IDR)",
      caption = "— Historis   ── Median   - - Mean   ░ Band 50%   ░░ Band 90%"
    ) +
    theme_minimal(base_family = "sans") +
    theme(
      plot.background  = element_rect(fill = "#ffffff", color = NA),
      panel.background = element_rect(fill = "#fafafa", color = NA),
      panel.grid.major = element_line(color = "#f1f5f9", linewidth = 0.4),
      panel.grid.minor = element_blank(),
      axis.text        = element_text(color = "#64748b", size = 9, face = "bold"),
      axis.title.y     = element_text(color = "#475569", size = 10, face = "bold"),
      plot.subtitle    = element_text(color = "#475569", size = 10, face = "bold"),
      plot.caption     = element_text(color = "#94a3b8", size = 9),
      plot.margin      = margin(12, 16, 8, 12)
    )
}

3.2 Fan Chart — Skenario 1.000 Iterasi

p_fan_1000 <- buat_fan_chart(
  sim_matrix    = sim_1000,
  tanggal       = tanggal_sim,
  S0_kurs       = T0,
  n_sim_label   = "1.000",
  warna_jalur   = "#3b82f6",
  warna_median  = "#1d4ed8",
  warna_band    = "#93c5fd"
)
p_fan_1000
Fan chart simulasi Monte Carlo GBM — 1.000 iterasi

Fan chart simulasi Monte Carlo GBM — 1.000 iterasi

# Simpan plot ke file
ggsave(
  filename = file.path(dir_plot, "fan_chart_1000_iterasi.png"),
  plot     = p_fan_1000,
  width    = 11, height = 6, units = "in", dpi = 300, bg = "white"
)
message("✅ Fan chart 1.000 iterasi disimpan.")

3.3 Fan Chart — Skenario 5.000 Iterasi

p_fan_5000 <- buat_fan_chart(
  sim_matrix    = sim_5000,
  tanggal       = tanggal_sim,
  S0_kurs       = T0,
  n_sim_label   = "5.000",
  warna_jalur   = "#8b5cf6",
  warna_median  = "#6d28d9",
  warna_band    = "#c4b5fd"
)
p_fan_5000
Fan chart simulasi Monte Carlo GBM — 5.000 iterasi

Fan chart simulasi Monte Carlo GBM — 5.000 iterasi

# Simpan plot ke file
ggsave(
  filename = file.path(dir_plot, "fan_chart_5000_iterasi.png"),
  plot     = p_fan_1000,
  width    = 11, height = 6, units = "in", dpi = 300, bg = "white"
)
message("✅ Fan chart 5.000 iterasi disimpan.")

3.4 Fan Chart — Skenario 10.000 Iterasi

p_fan_10000 <- buat_fan_chart(
  sim_matrix    = sim_10000,
  tanggal       = tanggal_sim,
  S0_kurs       = T0,
  n_sim_label   = "10.000",
  warna_jalur   = "#059669",
  warna_median  = "#065f46",
  warna_band    = "#6ee7b7"
)
p_fan_10000
Fan chart simulasi Monte Carlo GBM — 10.000 iterasi

Fan chart simulasi Monte Carlo GBM — 10.000 iterasi

# Simpan plot ke file
ggsave(
  filename = file.path(dir_plot, "fan_chart_10000_iterasi.png"),
  plot     = p_fan_1000,
  width    = 11, height = 6, units = "in", dpi = 300, bg = "white"
)
message("✅ Fan chart 10.000 iterasi disimpan.")

4 Tahap 2 — Distribusi Kurs Akhir (Hari ke-252)

Distribusi kurs pada hari terakhir simulasi (hari ke-252) menggambarkan kemungkinan nilai kurs USD/IDR satu tahun ke depan.

# Ambil kurs akhir dari setiap skenario
akhir_1000  <- sim_1000[nrow(sim_1000),   ]
akhir_5000  <- sim_5000[nrow(sim_5000),   ]
akhir_10000 <- sim_10000[nrow(sim_10000), ]

# Gabungkan untuk plotting
df_akhir <- data.frame(
  Kurs      = c(akhir_1000, akhir_5000, akhir_10000),
  Skenario  = factor(
    rep(c("1.000 iterasi", "5.000 iterasi", "10.000 iterasi"),
        c(length(akhir_1000), length(akhir_5000), length(akhir_10000))),
    levels = c("1.000 iterasi", "5.000 iterasi", "10.000 iterasi")
  )
)

# Simpan plot ke variabel p_dist
p_dist <- ggplot(df_akhir, aes(x = Kurs, fill = Skenario, color = Skenario)) +
  geom_density(alpha = 0.3, linewidth = 0.8) +
  geom_vline(xintercept = S0, color = "#1e293b",
             linewidth = 0.8, linetype = "dashed") +
  annotate("text", x = S0, y = Inf,
           label = sprintf("  S₀ = %s", formatC(S0, format="f", digits=0, big.mark=".")),
           vjust = 1.8, hjust = -0.05, color = "#1e293b",
           size = 3.5, fontface = "bold") +
  scale_x_continuous(
    labels = function(x) formatC(x, format="f", digits=0, big.mark=".")
  ) +
  scale_fill_manual(values  = c("#3b82f6","#8b5cf6","#059669")) +
  scale_color_manual(values = c("#1d4ed8","#6d28d9","#065f46")) +
  labs(x = "Kurs USD/IDR Hari ke-252 (IDR)",
       y = "Densitas",
       subtitle = "Distribusi kurs akhir simulasi — ketiga skenario overlay",
       fill = "Skenario", color = "Skenario") +
  theme_minimal(base_family = "sans") +
  theme(
    plot.background  = element_rect(fill = "#ffffff", color = NA),
    panel.background = element_rect(fill = "#fafafa", color = NA),
    panel.grid.major = element_line(color = "#f1f5f9", linewidth = 0.4),
    panel.grid.minor = element_blank(),
    axis.text  = element_text(color = "#64748b", size = 9,  face = "bold"),
    axis.title = element_text(color = "#475569", size = 10, face = "bold"),
    plot.subtitle   = element_text(color = "#475569", size = 10, face = "bold"),
    legend.position = "top",
    legend.text     = element_text(color = "#475569", size = 10, face = "bold"),
    plot.margin     = margin(12, 16, 8, 12)
  )

p_dist # Tampilkan plot di RStudio

# Simpan plot ke file secara spesifik menggunakan nama variabel
ggsave(
  filename = file.path(dir_plot, "distribusi_kurs_akhir.png"),
  plot     = p_dist,
  width    = 11, height = 6, units = "in", dpi = 300, bg = "white"
)
message("✅ Plot distribusi kurs akhir disimpan.")

5 Tahap 3 — Interval Kepercayaan 90%

# Hitung CI 90% dan statistik distribusi akhir
hitung_ci <- function(kurs_akhir, label) {
  data.frame(
    Skenario    = label,
    N_Iterasi   = length(kurs_akhir),
    Mean        = mean(kurs_akhir),
    Median      = median(kurs_akhir),
    SD          = sd(kurs_akhir),
    P05         = quantile(kurs_akhir, 0.05),
    P25         = quantile(kurs_akhir, 0.25),
    P75         = quantile(kurs_akhir, 0.75),
    P95         = quantile(kurs_akhir, 0.95),
    Min         = min(kurs_akhir),
    Max         = max(kurs_akhir)
  )
}

df_ci <- bind_rows(
  hitung_ci(akhir_1000,  "1.000 iterasi"),
  hitung_ci(akhir_5000,  "5.000 iterasi"),
  hitung_ci(akhir_10000, "10.000 iterasi")
)

# Format tabel
df_ci_fmt <- data.frame(
  Skenario  = df_ci$Skenario,
  Mean      = formatC(df_ci$Mean,   format="f", digits=0, big.mark="."),
  Median    = formatC(df_ci$Median, format="f", digits=0, big.mark="."),
  SD        = formatC(df_ci$SD,     format="f", digits=0, big.mark="."),
  CI_Bawah  = formatC(df_ci$P05,    format="f", digits=0, big.mark="."),
  CI_Atas   = formatC(df_ci$P95,    format="f", digits=0, big.mark="."),
  Min       = formatC(df_ci$Min,    format="f", digits=0, big.mark="."),
  Max       = formatC(df_ci$Max,    format="f", digits=0, big.mark=".")
)

kable(df_ci_fmt,
      caption = "Tabel 3.1 — Statistik Distribusi Kurs Akhir & Interval Kepercayaan 90%",
      align   = c("l","r","r","r","r","r","r","r"),
      col.names = c("Skenario","Mean","Median","Std Dev",
                    "P5 (CI Bawah)","P95 (CI Atas)","Minimum","Maksimum")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed","bordered"),
                full_width = TRUE) %>%
  column_spec(5, bold = TRUE, color = "#c0392b") %>%
  column_spec(6, bold = TRUE, color = "#27ae60") %>%
  column_spec(2, bold = TRUE, color = "#2c7bb6") %>%
  row_spec(3, background = "#eafaf1", bold = TRUE) %>%
  add_header_above(c(" " = 1, "Ukuran Pemusatan" = 2,
                     "Penyebaran" = 1, "Interval Kepercayaan 90%" = 2,
                     "Nilai Ekstrem" = 2))
Tabel 3.1 — Statistik Distribusi Kurs Akhir & Interval Kepercayaan 90%
Ukuran Pemusatan
Penyebaran
Interval Kepercayaan 90%
Nilai Ekstrem
Skenario Mean Median Std Dev P5 (CI Bawah) P95 (CI Atas) Minimum Maksimum
1.000 iterasi 17.197 17.176 919 15.768 18.718 14.329 20.817
5.000 iterasi 17.212 17.182 879 15.822 18.670 14.141 20.377
10.000 iterasi 17.218 17.204 865 15.837 18.668 14.000 20.704

6 Tahap 4 — Perbandingan Stabilitas Antar Skenario

Stabilitas dinilai dari seberapa konsisten statistik kunci (mean, median, CI) saat jumlah iterasi ditingkatkan dari 1.000 ke 10.000.

6.1 Plot Konvergensi Mean


6.1.1 2. Chunk konvergensi (Konvergensi Mean)

Blok dari {r konvergensi}` sampai ` lalu timpa dengan kode ini:

# Hitung mean kumulatif untuk setiap skenario
mean_kum_1000  <- cumsum(akhir_1000)  / seq_along(akhir_1000)
mean_kum_5000  <- cumsum(akhir_5000)  / seq_along(akhir_5000)
mean_kum_10000 <- cumsum(akhir_10000) / seq_along(akhir_10000)

# Subsample untuk plotting
idx_1000  <- round(seq(1, 1000,  length.out = 500))
idx_5000  <- round(seq(1, 5000,  length.out = 500))
idx_10000 <- round(seq(1, 10000, length.out = 500))

df_konv <- bind_rows(
  data.frame(iter = idx_1000,  mean_kum = mean_kum_1000[idx_1000],
             Skenario = "1.000 iterasi"),
  data.frame(iter = idx_5000,  mean_kum = mean_kum_5000[idx_5000],
             Skenario = "5.000 iterasi"),
  data.frame(iter = idx_10000, mean_kum = mean_kum_10000[idx_10000],
             Skenario = "10.000 iterasi")
) %>%
  mutate(Skenario = factor(Skenario,
    levels = c("1.000 iterasi","5.000 iterasi","10.000 iterasi")))

# Nilai teoretis GBM: E[S_T] = S0 * exp(mu * T)
mean_teoritis <- S0 * exp(mu * horizon)

# Simpan plot ke variabel p_konv
p_konv <- ggplot(df_konv, aes(x = iter, y = mean_kum, color = Skenario)) +
  geom_line(linewidth = 0.7, alpha = 0.85) +
  geom_hline(yintercept = mean_teoritis,
             color = "#ef4444", linewidth = 0.8,
             linetype = "dashed", alpha = 0.8) +
  annotate("text", x = max(df_konv$iter) * 0.6, y = mean_teoritis,
           label = sprintf("E[S_T] teoritis = %s",
             formatC(mean_teoritis, format="f", digits=0, big.mark=".")),
           vjust = -0.6, color = "#ef4444", size = 3.5, fontface = "bold") +
  scale_y_continuous(
    labels = function(x) formatC(x, format="f", digits=0, big.mark=".")
  ) +
  scale_color_manual(values = c("#3b82f6","#8b5cf6","#059669")) +
  labs(x = "Jumlah Iterasi (kumulatif)",
       y = "Mean Kurs Akhir (IDR)",
       subtitle = "Konvergensi mean kurs akhir menuju nilai ekspektasi teoretis GBM",
       color = "Skenario") +
  theme_minimal(base_family = "sans") +
  theme(
    plot.background  = element_rect(fill = "#ffffff", color = NA),
    panel.background = element_rect(fill = "#fafafa", color = NA),
    panel.grid.major = element_line(color = "#f1f5f9", linewidth = 0.4),
    panel.grid.minor = element_blank(),
    axis.text  = element_text(color = "#64748b", size = 9,  face = "bold"),
    axis.title = element_text(color = "#475569", size = 10, face = "bold"),
    plot.subtitle   = element_text(color = "#475569", size = 10, face = "bold"),
    legend.position = "top",
    legend.text     = element_text(color = "#475569", size = 10, face = "bold"),
    plot.margin     = margin(12, 16, 8, 12)
  )

p_konv # Tampilkan plot di RStudio

# Simpan plot ke file secara spesifik menggunakan nama variabel
ggsave(
  filename = file.path(dir_plot, "konvergensi_mean.png"),
  plot     = p_konv,
  width    = 11, height = 6, units = "in", dpi = 300, bg = "white"
)
message("✅ Plot konvergensi mean disimpan.")

6.2 Tabel Perbandingan Stabilitas

# Selisih CI antar skenario sebagai ukuran stabilitas
df_stab <- data.frame(
  Skenario     = c("1.000 iterasi","5.000 iterasi","10.000 iterasi"),
  Mean         = formatC(df_ci$Mean,   format="f", digits=0, big.mark="."),
  Median       = formatC(df_ci$Median, format="f", digits=0, big.mark="."),
  Lebar_CI90   = formatC(df_ci$P95 - df_ci$P05, format="f", digits=0, big.mark="."),
  CV_persen    = paste0(formatC((df_ci$SD / df_ci$Mean) * 100,
                                format="f", digits=2), "%"),
  Stabilitas   = c("Cukup stabil", "Stabil", "Sangat stabil")
)

kable(df_stab,
      caption = "Tabel 4.1 — Perbandingan Stabilitas Antar Skenario Simulasi",
      align   = c("l","r","r","r","r","c"),
      col.names = c("Skenario","Mean","Median",
                    "Lebar CI 90%","CV (%)","Stabilitas")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed","bordered"),
                full_width = TRUE) %>%
  column_spec(4, bold = TRUE) %>%
  column_spec(6, bold = TRUE,
              color = c("#e67e22","#2980b9","#27ae60")) %>%
  row_spec(3, background = "#eafaf1", bold = TRUE)
Tabel 4.1 — Perbandingan Stabilitas Antar Skenario Simulasi
Skenario Mean Median Lebar CI 90% CV (%) Stabilitas
1.000 iterasi 17.197 17.176 2.950 5.34% Cukup stabil
5.000 iterasi 17.212 17.182 2.848 5.11% Stabil
10.000 iterasi 17.218 17.204 2.831 5.02% Sangat stabil

7 Tahap 5 — Kesimpulan & Proyeksi

7.1 Nilai Ekspektasi Teoretis

# Ekspektasi GBM: E[S_T] = S0 * exp(mu * T)
E_ST       <- S0 * exp(mu * horizon)
median_ST  <- S0 * exp((mu - 0.5 * sigma^2) * horizon)

cat("=== PROYEKSI KURS USD/IDR (252 HARI KE DEPAN) ===\n\n")
## === PROYEKSI KURS USD/IDR (252 HARI KE DEPAN) ===
cat(sprintf("Kurs awal S₀                 : Rp %s\n",
    formatC(S0, format="f", digits=0, big.mark=".")))
## Kurs awal S₀                 : Rp 16.675
cat(sprintf("Ekspektasi E[S_T] teoretis   : Rp %s\n",
    formatC(E_ST, format="f", digits=0, big.mark=".")))
## Ekspektasi E[S_T] teoretis   : Rp 17.213
cat(sprintf("Median teoretis              : Rp %s\n",
    formatC(median_ST, format="f", digits=0, big.mark=".")))
## Median teoretis              : Rp 17.191
cat(sprintf("\nHasil simulasi (10.000 iter):\n"))
## 
## Hasil simulasi (10.000 iter):
cat(sprintf("  Mean   : Rp %s\n",
    formatC(mean(akhir_10000),   format="f", digits=0, big.mark=".")))
##   Mean   : Rp 17.218
cat(sprintf("  Median : Rp %s\n",
    formatC(median(akhir_10000), format="f", digits=0, big.mark=".")))
##   Median : Rp 17.204
cat(sprintf("  CI 90%% : Rp %s – Rp %s\n",
    formatC(quantile(akhir_10000, 0.05), format="f", digits=0, big.mark="."),
    formatC(quantile(akhir_10000, 0.95), format="f", digits=0, big.mark=".")))
##   CI 90% : Rp 15.837 – Rp 18.668

7.2 Tabel Kesimpulan Final

df_final <- data.frame(
  Komponen = c(
    "Kurs Awal S₀",
    "Ekspektasi Teoretis E[S_T]",
    "Median Teoretis",
    "Mean Simulasi (10.000 iter)",
    "Median Simulasi (10.000 iter)",
    "Batas Bawah CI 90% — P5",
    "Batas Atas CI 90% — P95",
    "Probabilitas kurs > S₀",
    "Skenario Terbaik Rekomendasi"
  ),
  Nilai = c(
    formatC(S0, format="f", digits=0, big.mark="."),
    formatC(E_ST, format="f", digits=0, big.mark="."),
    formatC(median_ST, format="f", digits=0, big.mark="."),
    formatC(mean(akhir_10000),   format="f", digits=0, big.mark="."),
    formatC(median(akhir_10000), format="f", digits=0, big.mark="."),
    formatC(quantile(akhir_10000, 0.05), format="f", digits=0, big.mark="."),
    formatC(quantile(akhir_10000, 0.95), format="f", digits=0, big.mark="."),
    paste0(round(mean(akhir_10000 > S0) * 100, 1), "%"),
    "10.000 iterasi"
  ),
  Satuan = c(
    "IDR per USD", "IDR per USD", "IDR per USD",
    "IDR per USD", "IDR per USD",
    "IDR per USD (batas bawah 90%)",
    "IDR per USD (batas atas 90%)",
    "probabilitas depresiasi IDR",
    "konvergensi optimal"
  )
)

kable(df_final,
      caption = "Tabel 5.1 — Ringkasan Proyeksi Kurs USD/IDR (Horizon 252 Hari)",
      align   = c("l","r","l")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed","bordered"),
                full_width = TRUE) %>%
  row_spec(1, bold = TRUE, background = "#eaf4fb") %>%
  row_spec(4, bold = TRUE, color = "#2c7bb6") %>%
  row_spec(6, bold = TRUE, color = "#c0392b") %>%
  row_spec(7, bold = TRUE, color = "#27ae60") %>%
  row_spec(9, bold = TRUE, background = "#eafaf1") %>%
  pack_rows("Nilai Referensi", 1, 3) %>%
  pack_rows("Hasil Simulasi", 4, 7) %>%
  pack_rows("Analisis Tambahan", 8, 9)
Tabel 5.1 — Ringkasan Proyeksi Kurs USD/IDR (Horizon 252 Hari)
Komponen Nilai Satuan
Nilai Referensi
Kurs Awal S₀ 16.675 IDR per USD
Ekspektasi Teoretis E[S_T] 17.213 IDR per USD
Median Teoretis 17.191 IDR per USD
Hasil Simulasi
Mean Simulasi (10.000 iter) 17.218 IDR per USD
Median Simulasi (10.000 iter) 17.204 IDR per USD
Batas Bawah CI 90% — P5 15.837 IDR per USD (batas bawah 90%)
Batas Atas CI 90% — P95 18.668 IDR per USD (batas atas 90%)
Analisis Tambahan
Probabilitas kurs > S₀ 73.1% probabilitas depresiasi IDR
Skenario Terbaik Rekomendasi 10.000 iterasi konvergensi optimal

7.3 Interpretasi Hasil

## === INTERPRETASI HASIL SIMULASI ===
## 1. PROYEKSI KURS
##    Berdasarkan simulasi 10.000 iterasi, kurs USD/IDR diproyeksikan
##    berada di kisaran Rp 15.837 – Rp 18.668 (CI 90%) dalam
##    252 hari perdagangan ke depan, dengan nilai tengah sekitar
##    Rp 17.218 per USD.
## 2. ARAH PERGERAKAN
##    Probabilitas kurs akhir > S₀ (depresiasi IDR): 73.1%
##    Probabilitas kurs akhir < S₀ (apresiasi IDR) : 26.9%
## 3. STABILITAS SIMULASI
##    Peningkatan iterasi dari 1.000 ke 10.000 menunjukkan konvergensi
##    yang baik pada mean dan interval kepercayaan. Skenario 10.000 iterasi
##    memberikan estimasi paling stabil dan direkomendasikan.
## 4. CATATAN PENTING
##    Model GBM mengasumsikan volatilitas dan drift konstan, serta tidak
##    memperhitungkan kejadian ekstrem (krisis, intervensi bank sentral).
##    Hasil ini merupakan proyeksi statistik, bukan prediksi pasti.

8 Ringkasan Keseluruhan Analisis

Ringkasan Keseluruhan Analisis Kurs USD/IDR
Bagian Judul Output Utama Status
Bagian 1 Data Wrangling df_usd_idr bersih (1.267 obs) ✅ Selesai |
Bagian 2 Statistik Deskriptif µ, σ, skewness, kurtosis log-return ✅ Selesai |
Bagian 3 Visualisasi (R Shiny) Dashboard interaktif 3 tab ✅ Selesai |
Bagian 4 Uji Normalitas Return KS-test & Jarque-Bera, GBM layak ✅ Selesai |
Bagian 5 Estimasi Parameter GBM µ = estimasi drift, σ = estimasi volatilitas ✅ Selesai |
Bagian 6 Simulasi Monte Carlo 3 matrix simulasi: 1K, 5K, 10K lintasan ✅ Selesai |
Bagian 7 Analisis Hasil Simulasi Fan chart, CI 90%, proyeksi kurs 1 tahun ✅ Selesai |

## 📁 Lokasi folder : C:/Users/anjan/Documents/Downloads/usd-idr-portfolio-github/usd-idr-gbm-montecarlo/scripts/output_plot
## 📊 Total file    : 5 plot tersimpan
## 
##  No                   Nama_File Ukuran_KB
##   1   distribusi_kurs_akhir.png     203.8
##   2  fan_chart_1000_iterasi.png    1454.2
##   3 fan_chart_10000_iterasi.png    1454.2
##   4  fan_chart_5000_iterasi.png    1454.2
##   5        konvergensi_mean.png     125.5

sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=English_United States.utf8 
## [2] LC_CTYPE=English_United States.utf8   
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C                          
## [5] LC_TIME=English_United States.utf8    
## 
## time zone: Asia/Jakarta
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] kableExtra_1.4.0 knitr_1.51       tidyr_1.3.1      ggplot2_4.0.2   
## [5] dplyr_1.2.0     
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       jsonlite_2.0.0     compiler_4.5.1     tidyselect_1.2.1  
##  [5] xml2_1.5.2         stringr_1.6.0      jquerylib_0.1.4    textshaping_1.0.5 
##  [9] systemfonts_1.3.2  scales_1.4.0       yaml_2.3.12        fastmap_1.2.0     
## [13] R6_2.6.1           labeling_0.4.3     generics_0.1.4     tibble_3.3.0      
## [17] svglite_2.2.2      bslib_0.10.0       pillar_1.11.1      RColorBrewer_1.1-3
## [21] rlang_1.1.7        stringi_1.8.7      cachem_1.1.0       xfun_0.56         
## [25] sass_0.4.10        S7_0.2.1           otel_0.2.0         viridisLite_0.4.2 
## [29] cli_3.6.5          withr_3.0.2        magrittr_2.0.4     digest_0.6.38     
## [33] grid_4.5.1         rstudioapi_0.18.0  lifecycle_1.0.5    vctrs_0.7.1       
## [37] evaluate_1.0.5     glue_1.8.0         farver_2.1.2       ragg_1.5.1        
## [41] rmarkdown_2.30     purrr_1.2.0        tools_4.5.1        pkgconfig_2.0.3   
## [45] htmltools_0.5.9

Dokumen ini merupakan bagian penutup dari seri analisis kurs USD/IDR menggunakan pendekatan Geometric Brownian Motion dan Simulasi Monte Carlo. Seluruh kode dapat direproduksi dengan menjalankan ulang semua bagian secara berurutan.