Final-test Optimasi

Optimalisasi Portofolio Crypto


*Kontak *\(\downarrow\)**
Email
Instagram https://www.instagram.com/cvnopp_/
RPubs https://rpubs.com/calvinriswandy/
Nama Calvin Riswandy
NIM 20214920003

Library

library(tidyverse) 
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(tidyquant) 
## Loading required package: PerformanceAnalytics
## Loading required package: xts
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## 
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Attaching package: 'xts'
## 
## The following objects are masked from 'package:dplyr':
## 
##     first, last
## 
## 
## Attaching package: 'PerformanceAnalytics'
## 
## The following object is masked from 'package:graphics':
## 
##     legend
## 
## Loading required package: quantmod
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(plotly)
## 
## Attaching package: 'plotly'
## 
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following object is masked from 'package:graphics':
## 
##     layout
library(timetk)
library(forcats) 

Pendahuluan

Seperti yang kita ketahui, perkembangan teknologi sangat perpengaruh besar bagi perkembangan ekonomi global. Perkembangan teknologi ini memudahkan kita dalam berinvestasi di perusahaan yang kita inginkan. Banyak investor saham khususnya yang merasa dimudahkan dengan perkembangan teknologi yang sangat pesat. Selain itu mulai muncul mata uang digital atau yang kita kenal sebagai cypto yang dimana mata uang ini sudah mulai menyebar dikalangan menengah keatas. Pada kesempatan ini saya ingin melakukan optimalisasi portofolio terhadap perkembangan Crypto secara global. Berikut ini adalah grafik yang menunjukan perkembangan salah satu mata uang crypto yaitu Etherium.

price_data <- tq_get('ETH-USD',
                      from = '2018-01-03',
                     to = '2023-07-01',
                     get = 'stock.prices')

price_data %>%
  plot_ly(x = ~date,
          type = 'candlestick',
          open = ~open,
          close = ~close,
          high = ~high,
          low = ~low) %>%
  layout(title = 'Harga Stok Market Etherium Sejak 2018-2023',
         xaxis = list(rangeslider = list(visible = F)))

Proses Optimalisasi Portofolio

para investor profesional juga mempertimbangkan proses optimalisasi portofolio dalam mengendalikan resiko investasi mereka. Teori optimalisasi portofolio yang dalam istilah bahasa ingris disebut Modern portfolio theory (MPT) menyatakan bahwa investor cendrung menghidari risiko, mereka akan memilih portofolio yang menawarkan pengembalian paling banyak. Untuk melakukan itu kita perlu mengoptimalkan portofolio, langkah yang harus dilakukan adalah

  1. Mengumpulkan data harga aset yang diplih.
  2. Memilih aset yang tidak beresiko dengan memperhatikan trend harga aset.
  3. Melakukan analisis korelasi antar aset yang akan dipilih.
  4. Menghitung pengembalian data periode waktu.
  5. Menetapkan bobot guna untuk membangun pembatas yang efisien.

Import data

Selanjutnya, saya memilih beberapa saham crypto untuk membangun portofolio dari komuditas saham berikut ini

  • Etherium (ETH)
  • Polygon (MATIC)
  • Chainlink (LINK)
  • Algorand (ALGO)
  • Monero (XMR)
# daftar data saham yg ingin diimport
tick <- c('ETH-USD','MATIC-USD','LINK-USD','ALGO-USD','XMR-USD')

price_data <- tq_get(tick,
                     from = '2018-01-03',
                     to = '2023-07-01',
                     get = 'stock.prices')

knitr::kable(head(price_data))
symbol date open high low close volume adjusted
ETH-USD 2018-01-03 886.000 974.471 868.451 962.720 5093159936 962.720
ETH-USD 2018-01-04 961.713 1045.080 946.086 980.922 6502859776 980.922
ETH-USD 2018-01-05 975.750 1075.390 956.325 997.720 6683149824 997.720
ETH-USD 2018-01-06 995.154 1060.710 994.622 1041.680 4662219776 1041.680
ETH-USD 2018-01-07 1043.010 1153.170 1043.010 1153.170 5569880064 1153.170
ETH-USD 2018-01-08 1158.260 1266.930 1016.050 1148.530 8450970112 1148.530
log_ret_tidy <- price_data %>%
  group_by(symbol) %>%
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn,
               period = 'daily',
               col_rename = 'ret',
               type = 'log')

knitr::kable(head(log_ret_tidy))
symbol date ret
ETH-USD 2018-01-03 0.0000000
ETH-USD 2018-01-04 0.0187304
ETH-USD 2018-01-05 0.0169797
ETH-USD 2018-01-06 0.0431175
ETH-USD 2018-01-07 0.1016799
ETH-USD 2018-01-08 -0.0040318
log_ret_xts <- log_ret_tidy %>%
  spread(symbol, value = ret) %>%
  tk_xts()
## Warning: Non-numeric columns being dropped: date
## Using column `date` for date_var.
knitr::kable(head(log_ret_xts))
ALGO-USD ETH-USD LINK-USD MATIC-USD XMR-USD
NA 0.000000000 0.00000000 NA 0.00000000
NA 0.018730362 0.36819232 NA -0.02212055
NA 0.016979704 -0.08132167 NA -0.04597857
NA 0.043117479 0.15870831 NA 0.16230937
NA 0.101679864 0.17774785 NA 0.01438460
NA -0.004031821 0.08074603 NA -0.12570129

Menghilangkan nilai NA atau null

Terdapat nilai NA atau null karena kurangnya nilai yang ditangkap pada kelompok tanggal waktu tersebut yaitu aset crypto tang tidak ada pada saat itu,

log_ret_xts[is.na(log_ret_xts)] <- 0
knitr::kable(head(log_ret_xts))
ALGO-USD ETH-USD LINK-USD MATIC-USD XMR-USD
0 0.000000000 0.00000000 0 0.00000000
0 0.018730362 0.36819232 0 -0.02212055
0 0.016979704 -0.08132167 0 -0.04597857
0 0.043117479 0.15870831 0 0.16230937
0 0.101679864 0.17774785 0 0.01438460
0 -0.004031821 0.08074603 0 -0.12570129

Membuat portofolio

Rata-rata Returns

mari hitung rata-rata pengembalian harian untuk setiap aset

mean_ret <- colMeans(log_ret_xts)
print(round(mean_ret, 5))
##  ALGO-USD   ETH-USD  LINK-USD MATIC-USD   XMR-USD 
##  -0.00161   0.00035   0.00112   0.00251  -0.00045

Menghitung Matriks Covarians

untuk menghitung matriks kovariansi aset crypto dilakukan dengan mengalikan dengan 252 dalam format tahunan.

cov_mat <- cov(log_ret_xts) * 252
print(round(cov_mat,4))
##           ALGO-USD ETH-USD LINK-USD MATIC-USD XMR-USD
## ALGO-USD    0.8073  0.3888   0.5013    0.5258  0.3450
## ETH-USD     0.3888  0.6062   0.5766    0.4531  0.4725
## LINK-USD    0.5013  0.5766   1.1259    0.5478  0.5037
## MATIC-USD   0.5258  0.4531   0.5478    1.2376  0.3957
## XMR-USD     0.3450  0.4725   0.5037    0.3957  0.6694

Menghitung weight atau bobot

wts <- runif(n = length(tick))
wts <- wts/sum(wts)
knitr::kable(wts)
x
0.2011034
0.0299947
0.1305853
0.2620712
0.3762454

Menghitung portofolio returns

port_returns <- (sum(wts * mean_ret) + 1)^252 - 1
knitr::kable(port_returns)
x
0.0844919

Menghitung portfolio risk

port_risk <- sqrt(t(wts) %*% (cov_mat %*% wts))
knitr::kable(port_risk)
0.7470949

Menghitung Sharpe Ratio

sharpe_ratio <- port_returns/port_risk
knitr::kable(sharpe_ratio)
0.1130939

Melakukan optimasi menggunakan 5000 portofolio acak

Selanjutnya dilakukan proses pembetukan portofolio secara acak dengan simulasi 5000 kali untuk memastikan signifikansinya secara statistik. Persiapkan vektor kosong untuk masing-masing langkah diatas.

Membuat matriks untuk menyimpan weight atau bobot

num_port <- 5000

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

Membuat matriks untuk menyimpan portofolio returns

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

Membuat matriks untuk menyimpan Standard deviation risk portofolio

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

Membuat matriks untuk menyimpan Portfolio Sharpe Ratio

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

Menjalankan perulangan sebanyak 5000 kali dan memastikan menggunakan function set.seed()agar bisa melakukan pengulangan.

Selanjutnya mari kita jalankan for loop 5000 kali

set.seed(5000)
for (i in seq_along(port_returns)) {
  
  wts <- runif(length(tick))
  wts <- wts/sum(wts)
  
  #store the weights in the matrix
  all_wts[i,] <- wts
  
  #portfolio returns
  port_ret <- sum(wts * mean_ret)
  port_ret <- ((port_ret + 1)^252) - 1
  
  #store the Portfolio Returns values
  port_returns[i] <- port_ret
  
  
  #create and store portfolio risk
  port_sd <- sqrt(t(wts) %*% (cov_mat  %*% wts))
  port_risk[i] <- port_sd
  
  #create and store Portfolio Sharpe Ratios
  #assume a 0% Risk free rate
  sr <- port_ret/port_sd
  sharpe_ratio[i] <- sr
  
}

Visualisasi Data

Membuat tabel semua yang berisi semua value

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

membuat objek deret waktu menjadi objek tibble, lalu ubah nama kolomnya

all_wts <- tk_tbl(all_wts)
## Warning in tk_tbl.data.frame(as.data.frame(data), preserve_index, rename_index,
## : Warning: No index to preserve. Object otherwise converted to tibble
## successfully.
colnames(all_wts) <- colnames(log_ret_xts)

menggabungkan semua value menjadi satu

portfolio_values <- tk_tbl(cbind(all_wts, portfolio_values))
## Warning in tk_tbl.data.frame(cbind(all_wts, portfolio_values)): Warning: No
## index to preserve. Object otherwise converted to tibble successfully.
#inspect the first few values
knitr::kable(head(portfolio_values))
ALGO-USD ETH-USD LINK-USD MATIC-USD XMR-USD Return Risk SharpeRatio
0.2223585 0.2192741 0.0630112 0.4881791 0.0071771 0.2897989 0.8289038 0.3496170
0.3069320 0.3430008 0.0032352 0.1401853 0.2066467 -0.0279606 0.7128172 -0.0392255
0.1196284 0.3183009 0.2946246 0.2170843 0.0503618 0.2142717 0.7789237 0.2750869
0.2554972 0.1970578 0.0860992 0.3645472 0.0967985 0.1705272 0.7777885 0.2192462
0.2983625 0.0213001 0.1560257 0.3410872 0.1832244 0.1275029 0.7829717 0.1628448
0.1800137 0.2518430 0.2103346 0.3006063 0.0572025 0.2117205 0.7771624 0.2724277

Plot minimum variance portfolio

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

p <- min_var %>%
  gather(`ALGO-USD`:`XMR-USD`, 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 = 'Assets', y = 'Weights', title = "Minimum Variance Portfolio Weights") +
  scale_y_continuous(labels = scales::percent) 

ggplotly(p, height = 300, width = 600)

Seperti yang dapat kita amati, portofolio Varians minumum tidak memiliki alokasi untuk LINK-USD dan MATIC-USD sangat sedikit. Mayoritas portofolio diinvestasikan di saham crypto XMR dan ETH.

Tengency portfolio

Selanjutnya, mari kita lihat portofolio tangency atau portofolio dengan Sharpe ratio tertinggi.

p <- max_sr %>%
  gather(`ALGO-USD`:`XMR-USD`, 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 = 'Assets', y = 'Weights', title = "Tangency Portfolio Weights") +
  scale_y_continuous(labels = scales::percent) 

ggplotly(p, height = 300, width = 600)

Batas Efisien Portofolio

dari plot ini menjelaskan semua portfolio acak dan memvisualisasikan batas efisiennya.

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 = 'Annualized Risk',
       y = 'Annualized Returns',
       title = "Portfolio Optimization & Efficient Frontier") +
  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.32, y = 0.26, label = "Tangency Portfolio") +
  annotate('text', x = 0.21, y = 0.13, label = "Minimum variance portfolio") +
  annotate(geom = 'segment', x = 0.00, xend = 0.00,  y = 0.00, 
           yend = 0.00, color = 'red', arrow = arrow(type = "open")) +
  annotate(geom = 'segment', x = 0.00, xend = 0.00,  y = 0.00, 
           yend = 0.00, color = 'red', arrow = arrow(type = "open"))


ggplotly(p, height = 500, width = 600)