Perbandingan Metode Regresi Nonparametrik dan Machine Learning untuk Memprediksi Harga Tutup Solana

Solana Logo

Pendahuluan

Latar Belakang

Pasar mata uang kripto dikenal sangat fluktuatif, sehingga prediksi harga penutupan (closing price) menjadi sangat penting untuk pengelolaan risiko, pengambilan keputusan investasi, dan evaluasi strategi trading. Dalam penelitian ini, Solana dipilih sebagai objek studi karena merupakan salah satu aset kripto dengan volume transaksi dan perhatian pasar yang tinggi.

Solana menarik untuk dikaji karena beberapa alasan utama. Pertama, sebagai blockchain berperforma tinggi, Solana mampu memproses ribuan transaksi per detik, mencerminkan kemajuan teknologi blockchain modern. Kedua, volatilitas harga Solana yang tinggi menjadikannya sangat cocok untuk menguji efektivitas metode regresi nonparametrik dalam menangkap pola non-linear pada data time series. Ketiga, kebutuhan akan model prediksi yang akurat sangat mendesak bagi investor dan trader untuk mengelola risiko di pasar kripto yang sangat dinamis.

Penelitian ini membandingkan pendekatan regresi nonparametrik dan Machine Learning yang fleksibel untuk memprediksi harga penutupan Solana berdasarkan data historis time series, yaitu regresi kernel Nadaraya-Watson dan model boosting tree LightGBM. Tujuannya adalah mengevaluasi efektivitas kedua metode dalam menangkap pola non-linear pada data harga kripto.

Tentang Solana

Solana adalah platform blockchain berperforma tinggi yang dirancang untuk mendukung aplikasi terdesentralisasi (dApps) dan cryptocurrency dengan skalabilitas tinggi. Dengan mekanisme konsensus Proof of Stake (PoS) dan Proof of History (PoH), Solana mampu memproses hingga 65.000 transaksi per detik dengan biaya transaksi yang sangat rendah.

Karakteristik Utama Solana:

  • Kecepatan: Transaksi diproses dalam hitungan detik
  • Biaya Rendah: Biaya transaksi sangat kompetitif
  • Skalabilitas: Mendukung ribuan dApps dan smart contracts
  • Ekosistem: Beragam proyek DeFi, NFT, dan Web3

Urgensi dan Kecocokan Metode:

Solana dipilih karena volatilitasnya yang tinggi, mencerminkan dinamika pasar kripto secara umum. Metode regresi nonparametrik seperti Nadaraya-Watson dan LightGBM sangat cocok untuk data kripto karena tidak memerlukan asumsi distribusi tertentu dan mampu menangkap pola non-linear yang kompleks. Dalam dunia cryptocurrency, harga sangat dipengaruhi oleh banyak faktor eksternal (sentimen pasar, regulasi, berita), metode nonparametrik memberikan fleksibilitas lebih tinggi dibandingkan metode parametrik tradisional.

Tujuan Penelitian

Penelitian ini bertujuan untuk:

  1. Mengembangkan model prediksi harga penutupan Solana menggunakan metode regresi nonparametrik dan Machine Learning.
  2. Membandingkan performa antara metode Nadaraya-Watson Kernel Regression dan LightGBM.
  3. Mengevaluasi efektivitas kedua metode dalam menangkap pola non-linear pada data time series harga kripto.
  4. Mengimplementasikan model dalam bentuk aplikasi interaktif untuk visualisasi dan analisis hasil.

Studi Kasus

Studi kasus pada penelitian ini adalah prediksi harga penutupan (close price) Solana menggunakan data historis time series dengan interval 1 jam. Target yang diprediksi adalah kolom close yang merepresentasikan harga penutupan pada setiap periode waktu.

Horizon Prediksi: Rolling forecast dengan data testing 3 observasi

Fitur yang Digunakan:

  • Informasi historis (lag features): open, high, low, volume dari periode sebelumnya
  • Fitur turunan: moving average, indikator volatilitas

Metode yang Dibandingkan:

  1. Regresi Kernel Nadaraya-Watson: Metode nonparametrik yang menggunakan fungsi kernel untuk estimasi nilai target berdasarkan bobot jarak antar titik.
  2. LightGBM: Algoritma gradient boosting yang sangat efisien untuk menangkap pola non-linear pada data time series.

Dataset dan Metode Pengambilan Data

Dataset yang digunakan berisi data harga historis Solana yang diperoleh dari platform KuCoin melalui API resmi. Data terdiri dari komponen OHLCV (Open, High, Low, Close, Volume) dengan interval waktu 1 jam.

Komposisi Dataset:

  • datetime: Tanggal dan waktu observasi
  • open: Harga pembukaan periode
  • high: Harga tertinggi periode
  • low: Harga terendah periode
  • close: Harga penutupan periode (target prediksi)
  • volume: Volume transaksi periode

Proses Pengambilan Data:

  1. Penarikan data historis melalui KuCoin API
  2. Penyelarasan format tanggal dan waktu
  3. Pengurutan data berdasarkan kronologi waktu
  4. Pembersihan data (missing value, duplikasi, outlier ekstrem)
  5. Validasi konsistensi data

Alur Proses Penelitian

Penelitian ini mengikuti alur proses sistematis sebagai berikut:

  1. Pengumpulan Data: Pengambilan data historis harga Solana dari KuCoin API.
  2. Preprocessing Data: Pembersihan dan transformasi data mentah menjadi format yang siap dianalisis.
  3. Eksplorasi Data: Analisis deskriptif dan visualisasi untuk memahami karakteristik data.
  4. Feature Engineering: Pembentukan fitur-fitur prediktif dari data historis.
  5. Pembagian Data: Split data menjadi training dan testing set secara temporal.
  6. Pengembangan Model: Training model Nadaraya-Watson dan LightGBM.
  7. Evaluasi Model: Pengukuran performa menggunakan metrik statistik.
  8. Visualisasi Hasil: Analisis komparatif dan interpretasi hasil.
  9. Rolling Forecast: Implementasi prediksi bertahap dengan update model.
  10. Kesimpulan: Temuan dan rekomendasi untuk pengembangan lanjutan.

Eksplorasi Data

Load Data

Data historis harga Solana dengan interval satu jam dimuat menggunakan fungsi pembacaan data yang mendukung efisiensi pada dataset berukuran besar. Selanjutnya dilakukan identifikasi kolom yang mencakup variabel waktu, harga pembukaan, harga tertinggi, harga terendah, harga penutupan, dan volume transaksi. Variabel waktu diparsing ke dalam format datetime dengan zona waktu yang konsisten, sementara seluruh variabel numerik dikonversi ke tipe numerik untuk menghindari kesalahan pembacaan akibat perbedaan format penulisan angka. Data kemudian diurutkan berdasarkan waktu untuk memastikan urutan kronologis yang benar sebagai dasar analisis deret waktu.

# LIBRARY SETUP
pkgs <- c("data.table", "lubridate", "ggplot2", "corrplot", "gridExtra", "lightgbm", "scales", "reshape2")
for (p in pkgs) {
  if (!require(p, character.only = TRUE)) install.packages(p, dependencies = TRUE)
  library(p, character.only = TRUE)
}
set.seed(42)
# DATA LOADING FUNCTIONS
to_num_id <- function(x) {
  if (is.numeric(x)) return(x)
  x <- trimws(as.character(x))
  x[x == ""] <- NA_character_
  
  dots <- gregexpr("\\.", x, perl = TRUE)
  ndots <- sapply(dots, function(m) if (length(m) == 1 && m[1] == -1) 0 else length(m))
  
  y <- x
  y[ndots >= 2] <- gsub("\\.", "", y[ndots >= 2])
  y <- gsub(",", "", y)
  y <- gsub(" ", "", y)
  
  suppressWarnings(as.numeric(y))
}

parse_dt_id <- function(x) {
  suppressWarnings(dmy_hm(as.character(x), tz = "UTC"))
}

# LOAD DATASET
path <- "D:/KULIAH NOVIA/SMT 5 NOVIA/Semiparametric Regression/pubs/UAS-REGRESI-SEMIPARAMETRIK/data solana 1 jam.csv"
raw <- fread(path, stringsAsFactors = FALSE)

cat("Jumlah Observasi:", nrow(raw), "\n")
## Jumlah Observasi: 6526
cat("Jumlah Variabel :", ncol(raw), "\n\n")
## Jumlah Variabel : 7
# INITIAL PREPROCESSING
need_cols <- c("datetime", "open", "high", "low", "close", "volume")
miss <- setdiff(need_cols, names(raw))
if (length(miss) > 0) stop("Kolom hilang: ", paste(miss, collapse = ", "))

raw[, ts := parse_dt_id(datetime)]
for (col in c("open", "high", "low", "close", "volume")) {
  raw[[col]] <- to_num_id(raw[[col]])
}

setorder(raw, ts)

Hasil prapemrosesan menunjukkan bahwa dataset terdiri dari 6526 observasi dengan struktur variabel yang lengkap dan konsisten. Seluruh kolom numerik berhasil dikonversi tanpa kehilangan data yang signifikan, dan variabel waktu telah terdefinisi dengan baik sehingga memungkinkan analisis temporal yang valid. Dengan urutan data yang telah terjaga dan tipe data yang sesuai, dataset berada dalam kondisi siap digunakan untuk tahap eksplorasi lanjutan maupun pemodelan deret waktu tanpa memerlukan penyesuaian struktural tambahan.

Statistik Deskriptif

Statistik deskriptif dihitung untuk variabel harga pembukaan, harga tertinggi, harga terendah, harga penutupan, serta volume transaksi. Ukuran yang digunakan meliputi nilai rata-rata, simpangan baku, nilai minimum, dan maksimum, dengan pengabaian nilai hilang jika ada. Perhitungan ini bertujuan memberikan ringkasan kuantitatif mengenai kecenderungan sentral dan tingkat penyebaran data sebagai dasar pemahaman awal sebelum analisis lebih lanjut. Selain itu, rentang waktu pengamatan ditentukan berdasarkan nilai minimum dan maksimum dari variabel waktu untuk memastikan cakupan periode analisis.

# SUMMARY STATISTICS
desc_stats <- raw[, .(
  Mean = sapply(.SD, mean, na.rm = TRUE),
  SD = sapply(.SD, sd, na.rm = TRUE),
  Min = sapply(.SD, min, na.rm = TRUE),
  Max = sapply(.SD, max, na.rm = TRUE)
), .SDcols = c("open", "high", "low", "close", "volume")]

print(desc_stats)
##            Mean           SD      Min         Max
##           <num>        <num>    <num>       <num>
## 1: 1.676802e+02 3.195245e+01   97.140 2.51350e+02
## 2: 1.686298e+02 3.207051e+01   98.853 2.53410e+02
## 3: 1.666666e+02 3.180465e+01   95.236 2.49810e+02
## 4: 1.676806e+02 3.194978e+01   97.135 2.51360e+02
## 5: 9.282127e+11 2.275700e+13 2977.690 1.83471e+15
cat("Periode Awal :", format(min(raw$ts, na.rm = TRUE), "%d %B %Y %H:%M"), "\n")
## Periode Awal : 12 March 2025 04:00
cat("Periode Akhir:", format(max(raw$ts, na.rm = TRUE), "%d %B %Y %H:%M"), "\n")
## Periode Akhir: 09 December 2025 01:00

Hasil statistik deskriptif menunjukkan bahwa rata-rata harga pembukaan, tertinggi, terendah, dan penutupan berada pada kisaran yang relatif berdekatan, dengan simpangan baku sekitar 31–32 USD, yang mencerminkan fluktuasi harga yang cukup besar sepanjang periode pengamatan. Rentang harga yang lebar, dari sekitar 95–99 USD hingga lebih dari 250 USD, mengindikasikan adanya fase pergerakan harga ekstrem dalam periode Maret hingga Desember 2025. Sementara itu, volume perdagangan memiliki variasi yang sangat tinggi, tercermin dari perbedaan yang jauh antara nilai rata-rata dan maksimum, yang menunjukkan distribusi volume yang sangat tidak merata dan kemungkinan keberadaan lonjakan aktivitas pasar pada waktu-waktu tertentu. Secara keseluruhan, statistik ini mengindikasikan dinamika pasar Solana yang aktif dengan fluktuasi harga dan volume yang signifikan sepanjang periode analisis.

Visualisasi Time Series

Visualisasi deret waktu dilakukan dengan memetakan harga penutupan Solana terhadap variabel waktu menggunakan grafik garis. Data mentah dengan interval satu jam digunakan tanpa agregasi untuk mempertahankan resolusi temporal asli. Grafik disusun dengan penyesuaian skala sumbu, format nilai harga dalam satuan dolar, serta pengaturan tampilan visual agar pola pergerakan harga dapat diamati secara jelas. Visualisasi ini bertujuan sebagai alat eksploratif untuk memahami dinamika harga sepanjang periode pengamatan sebelum dilakukan analisis lanjutan atau pemodelan.

# TIME SERIES VISUALIZATION - RAW DATA
ggplot(raw, aes(x = ts, y = close)) +
  geom_line(color = "#00FFA3", size = 0.5, alpha = 0.8) +
  labs(
    title = "Time Series Harga Penutupan Solana",
    subtitle = "Data Mentah - Interval 1 Jam",
    x = "Waktu",
    y = "Harga Close (USD)"
  ) +
  scale_y_continuous(labels = dollar_format(prefix = "$")) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

Grafik deret waktu harga penutupan Solana dengan interval satu jam menunjukkan dinamika harga yang fluktuatif dengan beberapa fase tren yang jelas sepanjang periode pengamatan. Pada awal periode, harga bergerak relatif stabil dengan kecenderungan naik, kemudian diikuti fase volatilitas yang lebih tinggi di pertengahan periode, ditandai oleh kenaikan harga yang cukup tajam hingga mencapai puncak sebelum mengalami koreksi signifikan. Setelah puncak tersebut, terlihat adanya tren penurunan yang berkelanjutan dengan volatilitas yang masih cukup tinggi, meskipun amplitudo fluktuasi cenderung mengecil mendekati akhir periode. Secara keseluruhan, grafik ini mencerminkan karakteristik pasar kripto yang sensitif terhadap sentimen dan informasi, dengan pergerakan harga yang cepat, tidak linear, serta menunjukkan adanya fase ekspansi dan kontraksi harga yang bergantian.

Analisis Korelasi

Analisis korelasi dilakukan dengan menghitung koefisien korelasi Pearson antara variabel harga pembukaan, harga tertinggi, harga terendah, harga penutupan, serta volume perdagangan. Perhitungan dilakukan menggunakan observasi lengkap untuk menghindari distorsi akibat nilai hilang. Hasil korelasi kemudian disajikan dalam bentuk matriks korelasi numerik dan divisualisasikan melalui heatmap untuk memudahkan identifikasi kekuatan dan arah hubungan antarvariabel secara visual.

# CORRELATION ANALYSIS
cor_matrix <- cor(raw[, .(open, high, low, close, volume)], use = "complete.obs")

print(cor_matrix)
##              open       high        low      close     volume
## open   1.00000000 0.99955622 0.99911834 0.99908711 0.01904072
## high   0.99955622 1.00000000 0.99896531 0.99956491 0.01912753
## low    0.99911834 0.99896531 1.00000000 0.99927949 0.01783072
## close  0.99908711 0.99956491 0.99927949 1.00000000 0.01880998
## volume 0.01904072 0.01912753 0.01783072 0.01880998 1.00000000
# Heatmap Korelasi
cor_melt <- melt(cor_matrix)
names(cor_melt) <- c("Var1", "Var2", "Correlation")

ggplot(cor_melt, aes(x = Var1, y = Var2, fill = Correlation)) +
  geom_tile(color = "white", size = 0.5) +
  geom_text(aes(label = sprintf("%.4f", Correlation)), 
            color = "black", size = 3.5) +
  scale_fill_gradient2(low = "blue", high = "red", mid = "white", 
                       midpoint = 0, limit = c(-1, 1), space = "Lab",
                       name = "Koefisien\nKorelasi") +
  labs(
    title = "Heatmap Korelasi Detail",
    x = "",
    y = ""
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 12),
    axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1),
    axis.text.y = element_text()
  )

  • open, high, low, close: Korelasi sangat tinggi (0.999+), menandakan harga-harga dalam interval 1 jam bergerak sangat sejalan.
  • volume dengan harga: Korelasi rendah, menunjukkan volume trading kurang berhubungan dengan pergerakan harga.
  • Korelasi sempurna antar harga OHLC mengindikasikan data sangat konsisten dalam interval waktu pendek.

Distribusi Close

Analisis distribusi harga penutupan dilakukan menggunakan histogram dan boxplot untuk menggambarkan pola penyebaran nilai harga secara keseluruhan. Histogram digunakan untuk mengamati bentuk distribusi, kecenderungan konsentrasi data, serta potensi skewness, sedangkan boxplot digunakan untuk mengidentifikasi posisi median, rentang antar kuartil, serta keberadaan nilai ekstrem. Kombinasi kedua visualisasi ini memberikan gambaran mengenai karakteristik distribusi harga penutupan Solana.

# DISTRIBUTION ANALYSIS
p1 <- ggplot(raw, aes(x = close)) +
  geom_histogram(fill = "#9945FF", alpha = 0.7, bins = 30) +
  labs(title = "Distribusi Harga Close", x = "Harga (USD)", y = "Frekuensi") +
  theme_minimal()

p2 <- ggplot(raw, aes(y = close)) +
  geom_boxplot(fill = "#14F195", alpha = 0.7) +
  labs(title = "Boxplot Harga Close", x = "", y = "Harga (USD)") +
  theme_minimal()

grid.arrange(p1, p2, ncol = 2)

histogram harga close menunjukkan distribusi yang tidak simetris sempurna, dengan konsentrasi utama pada rentang menengah dan ekor yang memanjang ke arah harga yang lebih tinggi, sehingga mengindikasikan skewness positif ringan. Ini berarti harga tinggi relatif lebih jarang tetapi tetap muncul. Boxplot memperkuat hal ini dengan median yang tidak berada tepat di tengah rentang serta adanya beberapa nilai ekstrem di sisi atas, yang menunjukkan variasi harga yang cukup besar dan keberadaan outlier.


Preprocessing

Feature Engineering

Pada tahap feature engineering, dilakukan pembentukan fitur lag satu periode (lag-1) untuk variabel open, high, low, dan volume dengan tujuan menangkap pola ketergantungan temporal dalam data deret waktu. Proses ini dilakukan dengan menggeser masing-masing variabel satu periode ke belakang sehingga merepresentasikan informasi historis yang tersedia sebelum periode prediksi. Selanjutnya, dataset disusun dengan memilih variabel waktu, fitur lag sebagai prediktor, dan harga penutupan sebagai variabel target. Observasi yang mengandung nilai hilang akibat proses pergeseran dieliminasi untuk memastikan kelengkapan data sebelum pemodelan.

# LAG FEATURE ENGINEERING
raw[, open_lag1   := shift(open, 1)]
raw[, high_lag1   := shift(high, 1)]
raw[, low_lag1    := shift(low, 1)]
raw[, volume_lag1 := shift(volume, 1)]

FEATURES <- c("open_lag1", "high_lag1", "low_lag1", "volume_lag1")
TARGET <- "close"

df <- raw[, c("ts", FEATURES, TARGET), with = FALSE]
df <- df[complete.cases(df)]

cat("Fitur yang digunakan (lag-1):\n")
## Fitur yang digunakan (lag-1):
for (i in 1:length(FEATURES)) {
  cat(i, ". ", FEATURES[i], "\n", sep = "")
}
## 1. open_lag1
## 2. high_lag1
## 3. low_lag1
## 4. volume_lag1
cat("\nTarget:", TARGET, "\n")
## 
## Target: close
cat("Observasi tersedia:", nrow(df), "\n")
## Observasi tersedia: 6525

Hasil feature engineering menunjukkan bahwa empat fitur lag berhasil dibentuk dan digunakan sebagai variabel prediktor untuk memodelkan harga penutupan periode saat ini. Setelah proses pembersihan data, diperoleh 6525 observasi dengan struktur data yang konsisten dan lengkap. Keberadaan fitur lag ini memungkinkan model untuk memanfaatkan informasi harga dan volume dari periode sebelumnya, sehingga diharapkan dapat meningkatkan kemampuan model dalam menangkap dinamika temporal pada pergerakan harga Solana.


Metodologi

Kernel Regression (Nadaraya-Watson)

Kernel Regression Nadaraya–Watson merupakan metode regresi nonparametrik yang digunakan untuk mengestimasi hubungan antara variabel prediktor dan variabel respon tanpa mengasumsikan bentuk fungsi tertentu. Estimasi nilai target dilakukan sebagai rata-rata tertimbang dari seluruh observasi data pelatihan, di mana bobot ditentukan oleh fungsi kernel berdasarkan kedekatan antara titik prediksi dan titik observasi. Dalam penelitian ini digunakan kernel Gaussian, yang memberikan bobot lebih besar pada observasi yang memiliki jarak lebih dekat terhadap titik yang diestimasi, sehingga struktur lokal data dapat tertangkap dengan baik. Parameter bandwidth berperan penting dalam mengontrol tingkat kehalusan estimasi, sehingga pemilihan bandwidth optimal dilakukan menggunakan metode Generalized Cross Validation (GCV) untuk menyeimbangkan bias dan varians serta menghindari kondisi overfitting maupun underfitting.

Rumus Estimator: \[ \hat{m}(x) = \frac{\sum_{i=1}^n K\left(\frac{x - X_i}{h}\right) Y_i}{\sum_{i=1}^n K\left(\frac{x - X_i}{h}\right)} \]

Kernel Gaussian: \[ K(u) = \frac{1}{\sqrt{2\pi}} e^{-\frac{1}{2}u^2} \]

GCV untuk Seleksi Bandwidth: \[ GCV(h) = \frac{MSE(h)}{[1 - \frac{1}{n}tr(S(h))]^2} \]

LightGBM (Gradient Boosting)

LightGBM merupakan algoritma pembelajaran ensemble berbasis gradient boosting yang membangun model prediksi melalui kombinasi sejumlah pohon keputusan secara bertahap. Setiap pohon baru dilatih untuk memperbaiki kesalahan prediksi dari model sebelumnya dengan meminimalkan fungsi kerugian. Pendekatan ini memungkinkan LightGBM untuk menangkap hubungan non-linear dan interaksi kompleks antar fitur secara efisien, bahkan pada dataset berukuran besar. Dalam implementasi ini, digunakan pengaturan parameter yang menekankan stabilitas dan generalisasi model, yaitu learning rate yang rendah untuk pembelajaran bertahap, jumlah daun yang moderat untuk mengontrol kompleksitas pohon, pembatasan kedalaman maksimum pohon, serta pemilihan sebagian fitur pada setiap iterasi untuk mengurangi risiko overfitting.

Update Model: \[ F_m(x) = F_{m-1}(x) + \gamma_m h_m(x) \]

Parameter yang digunakan:

  • learning_rate: 0.01
  • num_leaves: 31
  • max_depth: 6
  • feature_fraction: 0.9

Modelling

Train-Test Split

Data dibagi menjadi data pelatihan (train) dan data pengujian (test) menggunakan pendekatan pembagian temporal, di mana observasi awal digunakan sebagai training set dan observasi selanjutnya sebagai testing set. Proporsi pembagian ditetapkan sebesar 80% untuk pelatihan dan 20% untuk pengujian. Pendekatan ini dipilih untuk menjaga urutan waktu dan mencegah terjadinya data leakage, sehingga model hanya dilatih menggunakan informasi masa lalu ketika melakukan prediksi pada data masa depan. Selanjutnya, variabel prediktor dan variabel target dipisahkan serta dikonversi ke dalam bentuk matriks dan vektor numerik agar sesuai dengan kebutuhan metode pemodelan.

# TRAIN-TEST SPLIT
n <- nrow(df)
split_idx <- floor(0.8 * n)
train <- df[1:split_idx]
test  <- df[(split_idx + 1):n]

Xtr <- as.matrix(train[, ..FEATURES])
ytr <- as.numeric(train[[TARGET]])
Xte <- as.matrix(test[, ..FEATURES])
yte <- as.numeric(test[[TARGET]])

cat("Training :", nrow(train), "observasi (80%)\n")
## Training : 5220 observasi (80%)
cat("Testing  :", nrow(test), "observasi (20%)\n")
## Testing  : 1305 observasi (20%)

Hasil pembagian data menunjukkan bahwa training set terdiri dari 5220 observasi, sedangkan testing set terdiri dari 1305 observasi, dengan masing-masing memiliki empat variabel prediktor. Struktur data pada kedua subset konsisten dan siap digunakan dalam proses pelatihan serta evaluasi model. Pembagian data secara temporal ini memastikan bahwa kinerja model dievaluasi secara lebih objektif dan realistis, karena prediksi dilakukan pada data yang tidak digunakan selama proses pelatihan.

Model 1: Kernel Regression

Model Kernel Regression Nadaraya–Watson diimplementasikan untuk memodelkan hubungan nonparametrik antara fitur lag dan harga penutupan. Estimasi dilakukan menggunakan kernel Gaussian multivariat, di mana bobot observasi ditentukan berdasarkan jarak antara titik prediksi dan data pelatihan yang telah dinormalisasi oleh parameter bandwidth. Pemilihan bandwidth dilakukan secara data-driven menggunakan metode Generalized Cross Validation (GCV) untuk memperoleh tingkat kehalusan estimasi yang optimal. Bandwidth dihitung secara terpisah untuk setiap fitur dengan mempertimbangkan simpangan baku masing-masing variabel serta ukuran sampel pelatihan. Setelah bandwidth optimal diperoleh, model digunakan untuk menghasilkan prediksi pada data pelatihan dan data pengujian.

# KERNEL REGRESSION IMPLEMENTATION
kernel_predict_one <- function(x0, X, y, h) {
  x0 <- as.numeric(x0)
  U <- sweep(X, 2, x0, "-")
  U <- sweep(U, 2, h, "/")
  w <- exp(-0.5 * rowSums(U^2))
  sw <- sum(w)
  if (!is.finite(sw) || sw <= 1e-10) return(mean(y))
  sum(w * y) / sw
}

kernel_predict <- function(Xnew, X, y, h) {
  sapply(1:nrow(Xnew), function(i) kernel_predict_one(Xnew[i, ], X, y, h))
}

gcv_kernel <- function(X, y, h) {
  n <- nrow(X)
  yhat <- numeric(n)
  Sii  <- numeric(n)
  
  for (i in 1:n) {
    xi <- as.numeric(X[i, ])
    U <- sweep(X, 2, xi, "-")
    U <- sweep(U, 2, h, "/")
    w <- exp(-0.5 * rowSums(U^2))
    sw <- sum(w)
    
    if (sw <= 1e-10) {
      yhat[i] <- mean(y)
      Sii[i]  <- 0
    } else {
      yhat[i] <- sum(w * y) / sw
      Sii[i]  <- w[i] / sw
    }
  }
  
  mse <- mean((y - yhat)^2)
  trace_S <- mean(Sii)
  if (trace_S >= 0.99) trace_S <- 0.99
  mse / (1 - trace_S)^2
}

# BANDWIDTH SELECTION
n_tr <- nrow(Xtr)
d <- ncol(Xtr)
sd_vec <- apply(Xtr, 2, sd)
c_grid <- seq(0.3, 1.5, length.out = 15)

gcv_scores <- sapply(c_grid, function(c) {
  h_candidate <- c * sd_vec * n_tr^(-1/(d+4))
  gcv_kernel(Xtr, ytr, h = h_candidate)
})

best_c <- c_grid[which.min(gcv_scores)]
h_opt  <- best_c * sd_vec * n_tr^(-1/(d+4))

cat("Bandwidth Optimal (h):\n")
## Bandwidth Optimal (h):
for (i in 1:length(h_opt)) {
  cat(sprintf("  %-10s: %.6f\n", FEATURES[i], h_opt[i]))
}
##   open_lag1 : 3.419135
##   high_lag1 : 3.432352
##   low_lag1  : 3.402642
##   volume_lag1: 2617727933541.350586
cat("GCV Score Terbaik:", round(min(gcv_scores), 6), "\n")
## GCV Score Terbaik: 2.667124
# PREDICTION
pred_k_train <- kernel_predict(Xtr, Xtr, ytr, h_opt)
pred_k_test  <- kernel_predict(Xte, Xtr, ytr, h_opt)

Hasil seleksi bandwidth menunjukkan bahwa setiap fitur memiliki nilai bandwidth optimal yang berbeda, mencerminkan perbedaan skala dan kontribusi masing-masing variabel terhadap estimasi kernel. Bandwidth yang relatif kecil pada variabel harga menunjukkan sensitivitas yang lebih tinggi terhadap perubahan nilai fitur tersebut, sedangkan bandwidth yang besar pada variabel volume mencerminkan variasi skala yang jauh lebih besar. Nilai GCV minimum yang diperoleh menunjukkan bahwa model mampu mencapai keseimbangan antara bias dan varians, sehingga estimasi yang dihasilkan cukup fleksibel tanpa indikasi overfitting yang berlebihan. Prediksi yang dihasilkan pada data pelatihan dan pengujian selanjutnya digunakan untuk evaluasi kinerja model.

Model 2: LightGBM

Model LightGBM dilatih menggunakan data pelatihan dengan tujuan regresi untuk memprediksi harga penutupan berdasarkan fitur lag. Dataset pelatihan dikonversi ke dalam format khusus LightGBM agar proses pembelajaran berjalan efisien. Parameter model diatur untuk menyeimbangkan kemampuan menangkap pola non-linear dan pengendalian kompleksitas model, melalui penggunaan learning rate yang rendah, pembatasan kedalaman pohon, jumlah daun yang moderat, serta regularisasi L1 dan L2. Selain itu, teknik subsampling fitur dan data diterapkan untuk mengurangi risiko overfitting. Model dilatih secara iteratif hingga mencapai jumlah iterasi yang ditetapkan.

# LIGHTGBM IMPLEMENTATION
dtrain <- lgb.Dataset(Xtr, label = ytr)

params <- list(
  objective = "regression",
  metric = "rmse",
  learning_rate = 0.01,
  num_leaves = 31,
  max_depth = 6,
  min_data_in_leaf = 20,
  feature_fraction = 0.9,
  bagging_fraction = 0.9,
  bagging_freq = 1,
  lambda_l1 = 0.5,
  lambda_l2 = 0.5,
  seed = 42,
  verbose = -1
)

model_lgb <- lgb.train(params = params, data = dtrain, nrounds = 1000, verbose = -1)

pred_lgb_train <- predict(model_lgb, Xtr)
pred_lgb_test  <- predict(model_lgb, Xte)

cat("Training selesai. Jumlah iterasi:", model_lgb$current_iter(), "\n")
## Training selesai. Jumlah iterasi: 1000

Hasil pelatihan menunjukkan bahwa model LightGBM berhasil dikonvergensikan setelah 1.000 iterasi sesuai dengan pengaturan awal. Proses pelatihan berjalan stabil tanpa indikasi kegagalan pembelajaran, dan model mampu mempelajari hubungan non-linear serta interaksi antar fitur lag secara efektif. Dengan struktur model dan parameter yang telah ditetapkan, LightGBM siap digunakan untuk menghasilkan prediksi pada data pengujian dan dibandingkan kinerjanya dengan pendekatan nonparametrik lainnya.

Evaluasi Model

Evaluasi kinerja model dilakukan menggunakan empat metrik, yaitu Root Mean Squared Error (RMSE), Mean Absolute Error (MAE), Mean Absolute Percentage Error (MAPE), dan koefisien determinasi (R²). Keempat metrik tersebut digunakan untuk memberikan gambaran yang komprehensif mengenai tingkat kesalahan prediksi, baik dalam satuan absolut, persentase, maupun proporsi variasi data yang dapat dijelaskan oleh model. Perhitungan metrik dilakukan secara terpisah pada data pelatihan dan data pengujian untuk masing-masing model, yaitu Kernel Regression dan LightGBM, sehingga memungkinkan analisis perbandingan kinerja sekaligus deteksi potensi overfitting.

# EVALUATION FUNCTIONS
safe_mape <- function(y_true, y_pred, eps = 1e-8) {
  mean(abs((y_true - y_pred) / pmax(abs(y_true), eps))) * 100
}

eval_metrics <- function(y_true, y_pred) {
  data.table(
    RMSE = sqrt(mean((y_true - y_pred)^2)),
    MAE  = mean(abs(y_true - y_pred)),
    MAPE = safe_mape(y_true, y_pred),
    R2   = 1 - sum((y_true - y_pred)^2) / sum((y_true - mean(y_true))^2)
  )
}

# CALCULATE METRICS
metrics_k_train <- eval_metrics(ytr, pred_k_train)
metrics_k_test  <- eval_metrics(yte, pred_k_test)
metrics_l_train <- eval_metrics(ytr, pred_lgb_train)
metrics_l_test  <- eval_metrics(yte, pred_lgb_test)

comparison <- rbind(
  data.table(Model = "Kernel", Set = "Train", metrics_k_train),
  data.table(Model = "Kernel", Set = "Test", metrics_k_test),
  data.table(Model = "LightGBM", Set = "Train", metrics_l_train),
  data.table(Model = "LightGBM", Set = "Test", metrics_l_test)
)

print(comparison)
##       Model    Set     RMSE       MAE      MAPE        R2
##      <char> <char>    <num>     <num>     <num>     <num>
## 1:   Kernel  Train 1.613588 1.1628962 0.6942906 0.9976401
## 2:   Kernel   Test 1.741026 1.2637889 0.8005096 0.9949699
## 3: LightGBM  Train 1.350934 0.9820139 0.5882020 0.9983458
## 4: LightGBM   Test 1.623788 1.1549929 0.7325230 0.9956245

Hasil evaluasi menunjukkan bahwa kedua model memiliki performa yang baik dengan nilai kesalahan yang relatif rendah dan nilai R² yang tinggi pada data pelatihan maupun pengujian. Namun, LightGBM secara konsisten menghasilkan nilai RMSE, MAE, dan MAPE yang lebih rendah serta nilai R² yang sedikit lebih tinggi dibandingkan Kernel Regression pada data pengujian. Temuan ini mengindikasikan bahwa LightGBM lebih efektif dalam menangkap pola non-linear dan interaksi antar fitur lag pada data harga Solana. Selisih kinerja antara data pelatihan dan pengujian yang relatif kecil pada kedua model juga menunjukkan bahwa tidak terdapat indikasi overfitting yang signifikan, sehingga hasil evaluasi dapat dianggap cukup stabil dan representatif.

Visualisasi Perbandingan

Visualisasi perbandingan dilakukan untuk mengevaluasi kinerja prediksi kedua model secara visual dengan membandingkan nilai prediksi terhadap data aktual pada data pengujian. Perbandingan disajikan dalam bentuk grafik deret waktu untuk sejumlah observasi awal pada data testing, sehingga perbedaan pola prediksi antar model dapat diamati secara langsung. Visualisasi ini menampilkan tiga kurva, yaitu harga aktual, prediksi Kernel Regression, dan prediksi LightGBM, dengan skala dan format yang konsisten agar perbedaan respons model terhadap perubahan harga dapat dianalisis secara jelas.

# TIME SERIES COMPARISON (first 50 observations)
plot_data <- data.table(
  Index = 1:50,
  Actual = yte[1:50],
  Kernel = pred_k_test[1:50],
  LightGBM = pred_lgb_test[1:50]
)

ggplot(plot_data, aes(x = Index)) +
  geom_line(aes(y = Actual, color = "Aktual"), size = 1.2) +
  geom_line(aes(y = Kernel, color = "Kernel Regression"), size = 1, alpha = 0.8) +
  geom_line(aes(y = LightGBM, color = "LightGBM"), size = 1, alpha = 0.8) +
  labs(
    title = "Prediksi vs Aktual (50 Observasi Testing)",
    subtitle = "Perbandingan Dua Model",
    x = "Index Observasi",
    y = "Harga (USD)",
    color = "Model"
  ) +
  scale_color_manual(values = c("Aktual" = "#000000", 
                                "Kernel Regression" = "#FF6B6B", 
                                "LightGBM" = "#4ECDC4")) +
  scale_y_continuous(labels = dollar_format(prefix = "$")) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
    legend.position = "bottom"
  )

Hasil visualisasi menunjukkan bahwa kedua model mampu mengikuti pola umum pergerakan harga aktual dengan cukup baik pada periode pengujian. Namun, prediksi LightGBM tampak lebih responsif terhadap perubahan harga yang relatif cepat, terutama pada saat terjadi fluktuasi yang tajam, dibandingkan dengan Kernel Regression yang menghasilkan kurva prediksi lebih halus. Temuan ini mengindikasikan bahwa LightGBM memiliki kemampuan yang lebih baik dalam menangkap dinamika non-linear dan volatilitas jangka pendek, sementara Kernel Regression cenderung melakukan pemulusan (smoothing) terhadap pergerakan harga.

# SCATTER PLOTS
p1 <- ggplot(data.frame(Actual = yte, Kernel = pred_k_test), aes(x = Actual, y = Kernel)) +
  geom_point(color = "red", alpha = 0.5) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "gray40") +
  labs(title = "Kernel Regression", 
       x = "Aktual", 
       y = "Prediksi",
       caption = sprintf("R² = %.4f", metrics_k_test$R2)) +
  theme_minimal()

p2 <- ggplot(data.frame(Actual = yte, LightGBM = pred_lgb_test), aes(x = Actual, y = LightGBM)) +
  geom_point(color = "blue", alpha = 0.5) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "gray40") +
  labs(title = "LightGBM", 
       x = "Aktual", 
       y = "Prediksi",
       caption = sprintf("R² = %.4f", metrics_l_test$R2)) +
  theme_minimal()

grid.arrange(p1, p2, ncol = 2)

Scatter plot perbandingan nilai aktual dan prediksi menunjukkan bahwa kedua model menghasilkan prediksi yang sangat mendekati nilai aktual, yang tercermin dari titik-titik data yang terkonsentrasi di sekitar garis diagonal. Pola ini mengindikasikan tingkat akurasi yang tinggi serta kesalahan prediksi yang relatif kecil pada data pengujian. Dibandingkan dengan Kernel Regression, LightGBM memperlihatkan sebaran titik yang lebih rapat di sekitar garis referensi dan nilai R² yang sedikit lebih tinggi, yang menandakan presisi prediksi yang lebih baik. Temuan ini memperkuat hasil evaluasi kuantitatif sebelumnya bahwa LightGBM lebih efektif dalam menangkap variasi dan dinamika harga penutupan Solana.

# FEATURE IMPORTANCE
importance <- lgb.importance(model_lgb)

ggplot(importance, aes(x = reorder(Feature, Gain), y = Gain)) +
  geom_bar(stat = "identity", fill = "#45B7D1") +
  coord_flip() +
  labs(
    title = "Feature Importance - LightGBM",
    subtitle = "Kontribusi relatif setiap fitur dalam prediksi",
    x = "Fitur",
    y = "Importance (Gain)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5, color = "gray40")
  )

Visualisasi feature importance berdasarkan nilai gain menunjukkan bahwa variabel high_lag1 memiliki kontribusi paling dominan dalam proses prediksi harga penutupan Solana, diikuti oleh low_lag1, sementara open_lag1 memberikan kontribusi yang relatif lebih kecil. Sebaliknya, volume_lag1 memiliki pengaruh yang sangat terbatas terhadap prediksi model. Temuan ini mengindikasikan bahwa informasi harga ekstrem pada periode sebelumnya, khususnya harga tertinggi dan terendah, memainkan peran yang lebih penting dalam menentukan harga penutupan dibandingkan volume perdagangan, serta memperkuat hasil analisis sebelumnya mengenai rendahnya keterkaitan volume dengan pergerakan harga pada interval waktu pendek.


Rolling Forecast

Data Testing

Untuk mengevaluasi kemampuan model dalam kondisi prediksi real-time, dilakukan rolling forecast menggunakan 3 observasi data testing yang disediakan. Pendekatan ini mensimulasikan skenario operasional di mana model harus memprediksi harga penutupan secara bertahap, kemudian memperbarui dirinya dengan data aktual sebelum melakukan prediksi berikutnya.

# DATA TESTING
test_rolling <- data.table(
  datetime = c("12/9/25 2:00", "12/9/25 3:00", "12/9/25 4:00"),
  open = c(133.12, 132.79, 134.17),
  high = c(133.63, 134.4, 134.26),
  low = c(132.76, 132.5, 132.38),
  close = c(132.77, 134.14, 132.43),
  volume = c(1081972901486, 16187096, 10424055)
)

print(test_rolling)
##        datetime   open   high    low  close       volume
##          <char>  <num>  <num>  <num>  <num>        <num>
## 1: 12/9/25 2:00 133.12 133.63 132.76 132.77 1.081973e+12
## 2: 12/9/25 3:00 132.79 134.40 132.50 134.14 1.618710e+07
## 3: 12/9/25 4:00 134.17 134.26 132.38 132.43 1.042405e+07

Implementasi Rolling Forecast

Rolling forecast diimplementasikan dengan prosedur berikut: untuk setiap observasi testing, model dilatih menggunakan seluruh data training yang tersedia hingga titik waktu tersebut, kemudian menghasilkan prediksi untuk periode berikutnya. Setelah prediksi dibuat, nilai aktual dari observasi tersebut ditambahkan ke dalam training set untuk iterasi selanjutnya. Proses ini memastikan bahwa setiap model menggunakan informasi historis maksimal yang tersedia dan mencerminkan skenario prediksi real-world.

# INISIALISASI
results_rolling <- data.table(
  Iterasi = integer(),
  Datetime = character(),
  Actual = numeric(),
  Kernel_Pred = numeric(),
  LightGBM_Pred = numeric(),
  Kernel_Error = numeric(),
  LightGBM_Error = numeric()
)

train_current <- copy(train)

# ROLLING FORECAST LOOP
for (i in 1:nrow(test_rolling)) {
  cat("\n### ITERASI", i, "###\n")
  
  # 1. Ambil data testing ke-i
  test_row <- test_rolling[i, ]
  actual_close <- test_row$close
  
  # 2. Buat lag features
  if (i == 1) {
    last_train <- tail(train_current, 1)
    test_features <- data.table(
      open_lag1 = last_train$close,
      high_lag1 = test_row$high,
      low_lag1 = test_row$low,
      volume_lag1 = last_train$volume
    )
  } else {
    prev_test <- test_rolling[i-1, ]
    test_features <- data.table(
      open_lag1 = prev_test$close,
      high_lag1 = prev_test$high,
      low_lag1 = prev_test$low,
      volume_lag1 = prev_test$volume
    )
  }
  
  # 3. Ekstrak X dan y dari training
  X_train <- as.matrix(train_current[, ..FEATURES])
  y_train <- as.numeric(train_current[[TARGET]])
  
  # 4. KERNEL REGRESSION
  h_opt <- get_optimal_bandwidth(X_train, y_train)
  result_kernel <- kernel_predict_one_weighted(as.numeric(test_features), X_train, y_train, h_opt)
  kernel_pred <- result_kernel$pred
  kernel_error <- abs(actual_close - kernel_pred)
  
  cat("Kernel Regression:\n")
  cat("  Bandwidth:", paste(sprintf("%.6f", h_opt), collapse = ", "), "\n")
  cat("  Prediksi: $", round(kernel_pred, 4), "\n", sep = "")
  cat("  Error   : $", round(kernel_error, 4), "\n\n", sep = "")
  
  # 5. LIGHTGBM
  model_lgb <- train_lightgbm(X_train, y_train)
  lgb_pred <- predict(model_lgb, as.matrix(test_features))
  lgb_error <- abs(actual_close - lgb_pred)
  
  cat("LightGBM:\n")
  cat("  Prediksi: $", round(lgb_pred, 4), "\n", sep = "")
  cat("  Error   : $", round(lgb_error, 4), "\n\n", sep = "")
  
  cat("Actual Close: $", round(actual_close, 4), "\n", sep = "")
  
  # 6. Simpan hasil
  results_rolling <- rbind(results_rolling, data.table(
    Iterasi = i,
    Datetime = test_row$datetime,
    Actual = actual_close,
    Kernel_Pred = kernel_pred,
    LightGBM_Pred = lgb_pred,
    Kernel_Error = kernel_error,
    LightGBM_Error = lgb_error
  ))
  
  # 7. Update training data
  new_train_row <- data.table(
    ts = as.POSIXct(NA),
    open_lag1 = test_features$open_lag1,
    high_lag1 = test_features$high_lag1,
    low_lag1 = test_features$low_lag1,
    volume_lag1 = test_features$volume_lag1,
    close = actual_close
  )
  train_current <- rbind(train_current, new_train_row)
}
## 
## ### ITERASI 1 ###
## Kernel Regression:
##   Bandwidth: 3.419135, 3.432352, 3.402642, 2617727933541.350586 
##   Prediksi: $169.7691
##   Error   : $36.9991
## 
## LightGBM:
##   Prediksi: $132.8307
##   Error   : $0.0607
## 
## Actual Close: $132.77
## 
## ### ITERASI 2 ###
## Kernel Regression:
##   Bandwidth: 3.418953, 3.432347, 3.402621, 2617414926342.087402 
##   Prediksi: $132.9555
##   Error   : $1.1845
## 
## LightGBM:
##   Prediksi: $133.4178
##   Error   : $0.7222
## 
## Actual Close: $134.14
## 
## ### ITERASI 3 ###
## Kernel Regression:
##   Bandwidth: 3.418949, 3.432342, 3.402599, 2617101600068.970703 
##   Prediksi: $133.4925
##   Error   : $1.0625
## 
## LightGBM:
##   Prediksi: $133.7625
##   Error   : $1.3325
## 
## Actual Close: $132.43

Hasil Rolling Forecast

print(results_rolling)
##    Iterasi     Datetime Actual Kernel_Pred LightGBM_Pred Kernel_Error
##      <int>       <char>  <num>       <num>         <num>        <num>
## 1:       1 12/9/25 2:00 132.77    169.7691      132.8307    36.999083
## 2:       2 12/9/25 3:00 134.14    132.9555      133.4178     1.184515
## 3:       3 12/9/25 4:00 132.43    133.4925      133.7625     1.062474
##    LightGBM_Error
##             <num>
## 1:     0.06073585
## 2:     0.72223028
## 3:     1.33245943

Model Matematika

Berikut adalah representasi matematis dari model yang dihasilkan pada setiap iterasi rolling forecast:

Iterasi 1: Model Kernel Regression

## Formula Kernel Regression:
## ŷ = Σ(w_i × y_i) / Σ(w_i)
## Input Features:
##   open_lag1   = 197.4100
##   high_lag1   = 133.6300
##   low_lag1    = 132.7600
##   volume_lag1 = 32124669
## Bandwidth:
##   h_open_lag1 = 3.419135
##   h_high_lag1 = 3.432352
##   h_low_lag1 = 3.402642
##   h_volume_lag1 = 2617727933541.350586
## 
## Top 5 Bobot Tertinggi:
##   w_1 = 0.000192, y_1 = 122.0890
##   w_2 = 0.000192, y_2 = 122.0570
##   w_3 = 0.000192, y_3 = 123.1790
##   w_4 = 0.000192, y_4 = 125.3990
##   w_5 = 0.000192, y_5 = 124.5790
## 
## Model: ŷ = 169.7691

Iterasi 1: Model LightGBM

## Formula LightGBM:
## ŷ = base_score + η × Σ(tree_m(x))
## Parameter:
##   Learning rate (η) = 0.01
##   Jumlah trees      = 1000
##   Max depth         = 6
## Feature Importance:
##   high_lag1   : Gain = 0.7443
##   low_lag1    : Gain = 0.2551
##   open_lag1   : Gain = 0.0004
##   volume_lag1 : Gain = 0.0002
## 
## Model: ŷ = 132.8307

Pada iterasi berikutnya (iterasi 2 dan 3), model dilatih ulang dengan menambahkan data aktual dari iterasi sebelumnya, sehingga menghasilkan parameter dan bobot yang berbeda sesuai dengan informasi terbaru yang tersedia.

Evaluasi Performa Rolling Forecast

cat("EVALUASI PERFORMA ROLLING FORECAST\n\n")
## EVALUASI PERFORMA ROLLING FORECAST
cat("Kernel Regression:\n")
## Kernel Regression:
cat(sprintf("  MAE  : %.4f\n", mean(results_rolling$Kernel_Error)))
##   MAE  : 13.0820
cat(sprintf("  RMSE : %.4f\n", sqrt(mean(results_rolling$Kernel_Error^2))))
##   RMSE : 21.3812
cat(sprintf("  MAPE : %.2f%%\n",
            mean(abs(results_rolling$Kernel_Error / results_rolling$Actual)) * 100))
##   MAPE : 9.85%
cat("LightGBM:\n")
## LightGBM:
cat(sprintf("  MAE  : %.4f\n", mean(results_rolling$LightGBM_Error)))
##   MAE  : 0.7051
cat(sprintf("  RMSE : %.4f\n", sqrt(mean(results_rolling$LightGBM_Error^2))))
##   RMSE : 0.8757
cat(sprintf("  MAPE : %.2f%%\n",
            mean(abs(results_rolling$LightGBM_Error / results_rolling$Actual)) * 100))
##   MAPE : 0.53%
if (mean(results_rolling$LightGBM_Error) < mean(results_rolling$Kernel_Error)) {
  cat("Kesimpulan: LightGBM memiliki performa lebih baik dalam rolling forecast\n")
} else {
  cat("Kesimpulan: Kernel Regression memiliki performa lebih baik dalam rolling forecast\n")
}
## Kesimpulan: LightGBM memiliki performa lebih baik dalam rolling forecast

Visualisasi Rolling Forecast

plot_data_rolling <- data.table(
  Index = 1:3,
  Datetime = results_rolling$Datetime,
  Actual = results_rolling$Actual,
  Kernel = results_rolling$Kernel_Pred,
  LightGBM = results_rolling$LightGBM_Pred
)

ggplot(plot_data_rolling, aes(x = Index)) +
  geom_line(aes(y = Actual, color = "Actual"), size = 1.5) +
  geom_point(aes(y = Actual, color = "Actual"), size = 4) +
  geom_line(aes(y = Kernel, color = "Kernel Regression"), size = 1.2, linetype = "dashed") +
  geom_point(aes(y = Kernel, color = "Kernel Regression"), size = 3) +
  geom_line(aes(y = LightGBM, color = "LightGBM"), size = 1.2, linetype = "dashed") +
  geom_point(aes(y = LightGBM, color = "LightGBM"), size = 3) +
  labs(
    title = "Rolling Forecast: Prediksi vs Actual",
    subtitle = "3 Observasi Testing dengan Model Update Bertahap",
    x = "Iterasi",
    y = "Harga Close (USD)",
    color = "Model"
  ) +
  scale_color_manual(values = c("Actual" = "#000000", 
                                "Kernel Regression" = "#FF6B6B", 
                                "LightGBM" = "#4ECDC4")) +
  scale_y_continuous(labels = dollar_format(prefix = "$")) +
  scale_x_continuous(breaks = 1:3) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 14),
    plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
    legend.position = "bottom"
  )

Visualisasi rolling forecast menunjukkan bagaimana kedua model beradaptasi terhadap perubahan harga pada setiap iterasi. Model diperbarui secara bertahap menggunakan informasi terbaru, sehingga prediksi pada iterasi berikutnya dapat memanfaatkan pola yang diamati dari data aktual sebelumnya.


Kesimpulan

Ringkasan Hasil

Berdasarkan hasil evaluasi model pada data testing keseluruhan:

  1. Performa Terbaik: LightGBM dengan R² 0.9956 dibandingkan Kernel Regression 0.995.
  2. Akurasi: LightGBM memiliki nilai MAPE 0.73% (lebih kecil) dibandingkan Kernel Regression 0.8%.
  3. Fitur Penting: Fitur high_lag1 paling berpengaruh dalam prediksi harga.

Rolling Forecast

Hasil rolling forecast dengan 3 observasi menunjukkan:

  • Kernel Regression MAE: 13.082
  • LightGBM MAE: 0.7051
  • Model mampu beradaptasi dengan data baru dan menghasilkan prediksi yang akurat secara bertahap

Keterbatasan

  1. Hanya menggunakan data historis tanpa mempertimbangkan faktor eksternal (sentimen, berita, dll).
  2. Asumsi stationarity pada data time series mungkin tidak selalu terpenuhi.
  3. Rolling forecast hanya diuji pada 3 observasi.

Dashboard

Proyek ini juga dikembangkan lebih lanjut dalam bentuk dashboard Shiny interaktif yang menyajikan analisis dan hasil pemodelan secara lebih komprehensif. Dashboard tersebut memungkinkan pengguna untuk mengeksplorasi data historis, visualisasi, evaluasi model, serta hasil forecasting secara dinamis dan real time, sehingga memudahkan pemahaman terhadap perilaku harga Solana dan perbandingan kinerja model.

Akses Dashboard: https://vianarhma.shinyapps.io/UASRegresiSemiparametrik/


Developer

Projek ini disusun secara kelompok untuk memenuhi Tugas Akhir Ujian Akhir Semester Mata Kuliah Regresi Semiparametrik, Sains Data Universitas Muhammadiyah Semarang. Dengan rincian keanggotaan:

Nama NIM Peran
Novia Yunanita B2D023015 Pengelolaan dataset, reviewing hasil eksperimen, validasi alur analisis, penulisan publikasi.
Oktaviana Rahma Dhani B2D023022 Mengembangkan model LightGBM, Implementasi sistem Shiny
Dinda Putri Lestari B2D023017 Mengembangkan metode kernel regression, visualisasi hasil analisis dan forecasting.
Nurkhaliza B2D023021 Mengumpulkan landasan teori dan metodologi
Farid Sam Saputra B2D023020 Pengembangan Interface dan fitur Publikasi dan Dashboard