Final Test

Optimasi


Kontak : \(\downarrow\)
Email
Instagram https://www.instagram.com/saram.05/
RPubs https://rpubs.com/sausanramadhani/

Indonesia memiliki 40 indeks saham, salah satunya adalah LQ45. Indeks LQ45 adalah indeks saham yang ditetapkan oleh Bursa Efek Indonesia (BEI) pada Februari 1977. Indeks ini terdiri dari 45 perusahaan yang memenuhi kriteria tertentu, sebagai berikut:

  1. Termasuk dalam 60 perusahaan teratas dengan kapitalisasi pasar tertinggi dalam 12 bulan terakhir
  2. Termasuk dalam 60 perusahaan teratas dengan nilai transaksi tertinggi di pasar reguler dalam 12 bulan terakhir
  3. Telah tercatat di Bursa Efek Indonesia (BEI) selama minimal 3 bulan
  4. Memiliki kondisi keuangan, prospek pertumbuhan, dan nilai transaksi yang tinggi
  5. Mengalami penambahan bobot free float menjadi 100% yang sebelumnya hanya 60% dalam porsi penilaian

Import Data

Berdasarkan data saham LQ45, saya akan menganalisis 5 perusahaan dalam sektor FMCG (Fast-Moving Consumer Goods) atau lebih spesifik dalam sektor makanan dan minuman. Berikut ini 5 perusahaan yang akan di analisis menggunakan perhitungan yang telah dipelajari dalam mata kuliah optimasi:

  1. AMRT : Sumber Alfaria Trijaya Tbk.
  2. CPIN : Charoen Pokphand Indonesia Tbk.
  3. ICBP : Indofood CBP Sukses Makmur Tbk.
  4. INDF : Indofood Sukses Makmur Tbk.
  5. UNVR : Unilever Indonesia Tbk.

Untuk menganalisis data tersebut, mula-mula kita perlu memanggil data perusahaannya mulai dari tahun 2021 hingga 2023.

library(tidyquant)
library(plotly)
library(timetk)

fmcg <- c('AMRT.JK','CPIN.JK','ICBP.JK','INDF.JK','UNVR.JK')
# 'JK' untuk spesifikasi dalam memanggil data sahamnya
saham_fmcg <- tq_get(fmcg,
                     from = '2021-01-01',
                     to = Sys.Date(),
                     get = 'stock.prices')
saham_fmcg

Untuk melihat data teratas dari banyaknya data yang sudah terkumpul dapat menggunakan fungsi head().

head(saham_fmcg)

Disini terlihat bahwa data perusahaan diambil mulai dari 4 Januari 2021. Kemudian, untuk mengecek 6 data terbawah dapat menggunakan fungsi tail() sebagai berikut:

tail(saham_fmcg)

Dikarenakan tahun 2023 masih berjalan, maka coding import data hanya menyinkronkan dengan data saat ini yang sudah diperbolehkan untuk diakses publik. Dengan demikian, data perusahaan tersedia sampai 19 Desember 2023.

Return Saham

Untuk menghitung return saham harian dapat menggunakan pengembalian logaritmik. Dimana perhitungan ini juga memastikan data stasioner seperti berikut ini:

log_ret_tidy <- saham_fmcg %>%
  group_by(symbol) %>%
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn,
               period = 'daily',
               col_rename = 'ret',
               type = 'log')

library(DT)
datatable(log_ret_tidy)

Di atas merupakan hasil return saham hariannya, untuk lebih perjelas hasil baiknya dicari rata-rata return sahamnya. Sebelum itu, diperlukan pengubahan hasil return saham terlebih dahulu. Untuk mengubahnya menjadi format lebih lebar dapat menggunakan fungsi spread(). Kemudian, untuk mengubahnya menjadi objek time series dapat menggunakan fungsi xts(). Berikut ini penerapannya dalam R menggunakan spread() dan xts():

library(tidyr)

log_ret_xts <- log_ret_tidy %>%
  spread(symbol, value = ret) %>%
  tk_xts()

datatable(log_ret_xts)

Setelah mengubahnya menjadi format yang lebih besar dan menjadi objek time series, barulah dapat dicari rata-rata return sahamnya sebagai berikut:

mean_ret <- colMeans (log_ret_xts)
print(round(mean_ret, 4))
## AMRT.JK CPIN.JK ICBP.JK INDF.JK UNVR.JK 
##  0.0018 -0.0003  0.0002  0.0001 -0.0009

Berdasarkan hasil di atas, didapatkan rata-rata return saham masing-masing perusahaan. Posisi teratas yaitu Alfamart Retail Tbk dengan rata-rata return saham sebesar 0.0012. Maka perusahaan ini berdasarkan rata-ratanya telah banyak mengalami pengembalian saham.

Matriks Kovariansi

Untuk menghitung matriks kovarians, dapat mengalikannya dengan 252 dalam format tahunan sebagai berikut:

cov_mat <- cov(log_ret_xts)*252
print(round(cov_mat,4))
##         AMRT.JK CPIN.JK ICBP.JK INDF.JK UNVR.JK
## AMRT.JK  0.1825  0.0222  0.0007  0.0026  0.0058
## CPIN.JK  0.0222  0.1065  0.0155  0.0153  0.0190
## ICBP.JK  0.0007  0.0155  0.0626  0.0241  0.0238
## INDF.JK  0.0026  0.0153  0.0241  0.0464  0.0179
## UNVR.JK  0.0058  0.0190  0.0238  0.0179  0.1025

Risiko Portofolio Saham

Sebelum mencari risiko saham, perlu mencari bobot acak dan rata-rata pengembalian aset terlebih dahulu.

wts <- runif(n = length(fmcg))
wts <- wts/sum(wts)

port_returns <- (sum(wts*mean_ret)+1)^252-1

Setelah itu, barulah bisa mencari risiko portofolio saham sebagai berikut:

port_risk <- sqrt(t(wts)%*%(cov_mat%*%wts))
port_risk
##           [,1]
## [1,] 0.1971547

Didapatkan nilai risiko portofolio sahamnya sebesar 0.2372685 yang mana merupakan standar deviasi dari portofolio saham berdasarkan bobot dan kovarian yang sudah dihitung. Nilai tersebut mengartikan besarnya fluktuasi harga (perubahan harga harian) yang diharapkan dari portofolio ini.

Bobot Portofolio

Berikut ini untuk mencari bobot portofolio saham harian:

sharpe_ratio <- port_returns/port_risk
sharpe_ratio
##           [,1]
## [1,] 0.7600705

Dengan menggunakan Sharpe Ratio, didapatkan nilai bobot portofolio saham sebesar 0.45. Namun, ini bukanlah perhitungan bobot portofolio optimum. Nilai ini hanya penentuan performa portofolio yang mana dengan nilai 0.45048 artinya 45% dari nilai total portofolio diberikan kepada suatu investasi/aset tertentu.

Diperlukan pembentukan portofolio secara acak dengan simulasi 5000 kali. Hal ini bertujuan untuk memastikan signifikansinya secara statistik.

num_port <- 5000
all_wts <- matrix(nrow = num_port, ncol = length(fmcg))

port_returns <- vector('numeric', length = num_port)
port_risk <- vector('numeric', length = num_port)
sharpe_ratio <- vector('numeric', length = num_port)

Kemudian dapat dijalankan for loop 5000 kali, sebagai berikut:

for (i in seq_along(port_returns)) {
  
  wts <- runif(length(fmcg))
  wts <- wts/sum(wts)
  
  all_wts[i,] <- wts
  
  
  port_ret <- sum(wts * mean_ret)
  port_ret <- ((port_ret + 1)^252) - 1
  
  port_returns[i] <- port_ret
  
  
  port_sd <- sqrt(t(wts) %*% (cov_mat  %*% wts))
  port_risk[i] <- port_sd
  
  sr <- port_ret/port_sd
  sharpe_ratio[i] <- sr
  
}

Kemudian dibuatlah tabel data guna menyimpan semua nilai secara bersamaan.

portfolio_values <- tibble(Return = port_returns,
                           Risk = port_risk,
                           SharpeRatio = sharpe_ratio)

all_wts <- tk_tbl(all_wts)
colnames(all_wts) <- colnames(log_ret_xts)
portfolio_values <- tk_tbl(cbind(all_wts, portfolio_values))

datatable(portfolio_values)

Setelah memiliki bobot tiap aset dengan risiko dan pengembalian bersama menggunakan Sharpe Ratio dari tiap portofolio. Barulah kita bisa lihat portofolio variansi minimum dan portofolio sharpe ratio optimum. Hingga nantinya semua bisa di visualisasikan sesuai dengan batas efisiennya.

Bobot Portofolio Variansi Minimum

library(forcats)

min_var <- portfolio_values[which.min(portfolio_values$Risk),]

p <- min_var %>%
  gather(AMRT.JK:UNVR.JK, key=Asset,
         value = Weights) %>%
  mutate(Asset = as.factor(Asset)) %>%
  ggplot(aes(x=fct_reorder(Asset,Weights), y=Weights, fill=Asset)) +
  geom_bar(stat = 'identity') +
  theme_minimal() +
  labs(x = 'Aset',
       y = 'Bobot',
       title = "Bobot Portofolio dengan Variansi Minimum") +
  scale_y_continuous(labels = scales::percent) +
  theme(legend.position = "none")

ggplotly(p)

Berdasarkan hasilnya, portofolio Variansi Minimum tidak memiliki alokasi untuk Charoen Pokphand Indonesia Tbk (CPIN.JK) dan Unilever Indonesia (UNVR.JK) sangat sedikit. Mayoritas portofolio diinvestasikan di saham Indofood Sukses Makmur Tbk (INDF.JK) dan Indofood CBP Sukses Makmur Tbk (ICBP.JK).

Bobot Portofolio Optimum (Portofolio Tangensi)

max_sr <- portfolio_values[which.max(portfolio_values$SharpeRatio),]

p <- max_sr %>%
  gather(AMRT.JK:UNVR.JK, key = Asset,
         value = Weights) %>%
  mutate(Asset = as.factor(Asset)) %>%
  ggplot(aes(x=fct_reorder(Asset,Weights), y=Weights, fill=Asset)) +
  geom_bar(stat = 'identity') +
  theme_minimal() +
  labs(x = 'Aset',
       y = 'Bobot',
       title = "Bobot Portofolio Tangensi (Maksimum Sharpe Ratio") +
  scale_y_continuous(labels = scales::percent) +
  theme(legend.position = "none")

ggplotly(p)

Didapatkan bobot portofolio dengan maksimum sharpe ratio yaitu pada Alfamart Retail Tbk (AMRT.JK) sebesar 68%.

Batas Efisien Portofolio

Setelah didapatkan variansi minimum dan maksimumnya, barulah dapat divisualisasikan sebagai berikut:

p <- portfolio_values %>%
  ggplot(aes(x = Risk, y = Return, color = SharpeRatio)) +
  geom_point() +
  theme_classic() +
  scale_y_continuous(labels = scales::percent) +
  scale_x_continuous(labels = scales::percent) +
  labs(x = 'Risiko Tahunan',
       y = 'Pengembalian Tahunan',
       title = "Optimasi Portofolio & Perbatasan yang Efisien") +
  geom_point(aes(x = Risk,y = Return), data = min_var, color = 'red') +
  geom_point(aes(x = Risk,y = Return), data = max_sr, color = 'red') +
  annotate('text', x = 0.329, y = 0.78, label = "Portofolio Tangensi") +
  annotate('text', x = 0.287, y = 0.429, label = "Portofolio Varians minimum") +
  annotate(geom = 'segment', x = 0.3163, xend = 0.3163,  y = 0.659, 
           yend = 0.74, color = 'red', arrow = arrow(type = "open")) +
  annotate(geom = 'segment', x = 0.2383, xend = 0.2383,  y = 0.328, 
           yend = 0.40, color = 'red', arrow = arrow(type = "open"))
  

ggplotly(p)