Final Test
Optimasi
| Kontak | : \(\downarrow\) |
| mugemisausan05@gmail.com | |
| 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:
- Termasuk dalam 60 perusahaan teratas dengan kapitalisasi pasar
tertinggi dalam 12 bulan terakhir
- Termasuk dalam 60 perusahaan teratas dengan nilai transaksi
tertinggi di pasar reguler dalam 12 bulan terakhir
- Telah tercatat di Bursa Efek Indonesia (BEI) selama minimal 3 bulan
- Memiliki kondisi keuangan, prospek pertumbuhan, dan nilai transaksi
yang tinggi
- 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:
- AMRT : Sumber Alfaria Trijaya Tbk.
- CPIN : Charoen Pokphand Indonesia Tbk.
- ICBP : Indofood CBP Sukses Makmur Tbk.
- INDF : Indofood Sukses Makmur Tbk.
- 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_fmcgUntuk melihat data teratas dari banyaknya data yang sudah terkumpul
dapat menggunakan fungsi head().
Disini terlihat bahwa data perusahaan diambil mulai dari 4 Januari
2021. Kemudian, untuk mengecek 6 data terbawah dapat menggunakan fungsi
tail() sebagai berikut:
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:
## 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:
## 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.
Setelah itu, barulah bisa mencari risiko portofolio saham sebagai berikut:
## [,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:
## [,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)