Final-test Optimasi
Optimalisasi Portofolio Crypto
| *Kontak | *\(\downarrow\)** |
| calvin.riswandy@gmail.com | |
| https://www.instagram.com/cvnopp_/ | |
| RPubs | https://rpubs.com/calvinriswandy/ |
| Nama | Calvin Riswandy |
| NIM | 20214920003 |
Library
## ── 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
## 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
##
## 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
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
- Mengumpulkan data harga aset yang diplih.
- Memilih aset yang tidak beresiko dengan memperhatikan trend harga aset.
- Melakukan analisis korelasi antar aset yang akan dipilih.
- Menghitung pengembalian data periode waktu.
- 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 |
## Warning: Non-numeric columns being dropped: date
## Using column `date` for date_var.
| 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,
| 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
## 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.
## 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
| x |
|---|
| 0.2011034 |
| 0.0299947 |
| 0.1305853 |
| 0.2620712 |
| 0.3762454 |
Menghitung portofolio returns
| x |
|---|
| 0.0844919 |
Menghitung portfolio risk
| 0.7470949 |
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
Membuat matriks untuk menyimpan portofolio returns
Membuat matriks untuk menyimpan
Standard deviation risk portofolio
Membuat matriks untuk menyimpan
Portfolio Sharpe Ratio
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_valuesmembuat objek deret waktu menjadi objek tibble, lalu ubah nama kolomnya
## 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.
menggabungkan semua value menjadi satu
## Warning in tk_tbl.data.frame(cbind(all_wts, portfolio_values)): Warning: No
## index to preserve. Object otherwise converted to tibble successfully.
| 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)