#install.packages("quantmod")
library(quantmod)
## Warning: package 'quantmod' was built under R version 4.3.3
## Loading required package: xts
## Warning: package 'xts' was built under R version 4.3.3
## Loading required package: zoo
## Warning: package 'zoo' was built under R version 4.3.1
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## Loading required package: TTR
## Warning: package 'TTR' was built under R version 4.3.3
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## [1] "BBRI.JK" "TBIG.JK" "WIKA.JK"
##            BBRI.JK.Close
## 2022-01-03          4180
## 2022-01-04          4160
## 2022-01-05          4210
## 2022-01-06          4160
## 2022-01-07          4190
## 2022-01-10          4180
##            WIKA.JK.Close
## 2022-01-03          1140
## 2022-01-04          1140
## 2022-01-05          1115
## 2022-01-06          1130
## 2022-01-07          1170
## 2022-01-10          1160
##            TBIG.JK.Close
## 2022-01-03          3050
## 2022-01-04          3010
## 2022-01-05          2930
## 2022-01-06          2830
## 2022-01-07          2830
## 2022-01-10          2820
returns <- do.call(merge,
                   lapply(stocks,
                          function(x) dailyReturn(get(x))))
colnames(returns) <- stocks
head(round(returns, 4))
##            BBRI.JK TBIG.JK WIKA.JK
## 2022-01-03  0.0146  0.0304  0.0270
## 2022-01-04 -0.0048 -0.0131  0.0000
## 2022-01-05  0.0120 -0.0266 -0.0219
## 2022-01-06 -0.0119 -0.0341  0.0135
## 2022-01-07  0.0072  0.0000  0.0354
## 2022-01-10 -0.0024 -0.0035 -0.0085
volatility = sd(returns)
volatility
## [1] 0.02752645
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.3.3
autoplot(returns) +
labs(title = "Stock Portfolio Returns in IDX",
y = "Daily Return")

weights <- c(0.4, 0.3, 0.2, 0.1)
port_return <- sum(weights * colMeans(returns,
na.rm = TRUE))
## Warning in weights * colMeans(returns, na.rm = TRUE): longer object length is
## not a multiple of shorter object length
port_return
## [1] -0.0002932245
#install.packages("PerformanceAnalytics")
library(PerformanceAnalytics)
## Warning: package 'PerformanceAnalytics' was built under R version 4.3.3
## 
## Attaching package: 'PerformanceAnalytics'
## The following object is masked _by_ '.GlobalEnv':
## 
##     weights
## The following object is masked from 'package:graphics':
## 
##     legend
library(ggplot2)
# Simulating random portfolio weights for 5 IDX stocks
set.seed(1234)
returns <- matrix(rnorm(2000, 0.01, 0.02), ncol = 5)
cov_matrix <- cov(returns)
returns <- matrix(colMeans(returns))
weights <- matrix(runif(500, 0, 1), ncol = 5)
weights <- weights / rowSums(weights)
# Compute expected return and risk
port_returns <- rowSums(weights %*% returns)
port_risk <- sqrt(rowSums((weights %*% cov_matrix)
* weights))
# Efficient Frontier Plot
ggplot(data.frame(port_risk, port_returns),
aes(x = port_risk, y = port_returns)) +
geom_point(color = "blue", alpha = 0.5) +
labs(title = "Efficient Frontier",
x = "Risk (Std Dev)",
y = "Expected Return")

#Grafik tersebut menunjukkan efficient frontier, yaitu kumpulan portofolio yang memberikan return terbaik untuk tingkat risiko tertentu, atau risiko terendah untuk tingkat return tertentu. Sumbu horizontal mewakili tingkat risiko portofolio (diukur dengan standar deviasi return), sedangkan sumbu vertikal menunjukkan expected return dari portofolio. Titik-titik biru pada grafik mewakili kombinasi portofolio yang dihasilkan dari berbagai proporsi aset. Semakin ke kiri atas posisi titik, semakin efisien portofolio tersebut, artinya investor bisa mendapatkan return lebih tinggi dengan risiko lebih rendah. Grafik ini membantu investor memilih portofolio optimal sesuai preferensi antara risiko dan imbal hasil yang diinginkan.
library(quantmod)
library(PerformanceAnalytics)
library(ggplot2)

# Data saham yang diambil fedh
stocks <- c("BBRI.JK", "TBIG.JK", "WIKA.JK")
getSymbols(stocks, src = "yahoo", from = "2022-01-01", to = "2024-12-31")
## [1] "BBRI.JK" "TBIG.JK" "WIKA.JK"
# Return harian
returns_data <- do.call(merge,
                        lapply(stocks,
                               function(x) dailyReturn(Cl(get(x)))))
colnames(returns_data) <- stocks

# Simulasi random portofolio berdasarkan data asli 
# Karena di materi bapak nya pake ncol=5 dan itu angka acak saat dibangkitkan 
# Padahal tgs hanya 3 saham dan sudah ditentukan jadi remake bagian
set.seed(1234)
returns <- coredata(returns_data)  # Data returns saham
cov_matrix <- cov(returns, use = "complete.obs")
mean_returns <- colMeans(returns, na.rm = TRUE)

n_portfolios <- 5000  # Banyak portofolio disimulasikan
weights <- matrix(runif(n_portfolios * length(stocks), 0, 1), ncol = length(stocks)) #ini bagian remake
weights <- weights / rowSums(weights)

# Return dan risiko portofolio
port_returns <- weights %*% mean_returns
port_risk <- sqrt(rowSums((weights %*% cov_matrix) * weights))

ggplot(data.frame(port_risk, port_returns),
       aes(x = port_risk, y = port_returns)) +
  geom_point(color = "blue", alpha = 0.5) +
  labs(title = "Efficient Frontier",
       x = "Risk (Std Dev)",
       y = "Expected Return")

# Sharpe Ratio
risk_free_rate <- 0.0575  # dari BI
sharpe_ratio <- (port_returns - risk_free_rate) / port_risk

# Cari portofolio optimal dari Sharpe Ratio tertinggi
best_index <- which.max(sharpe_ratio)
best_weights <- weights[best_index, ]

# Plot Efficient Frontier versi highlight titik optimal
df <- data.frame(Risk = port_risk, Return = port_returns)

ggplot(df, aes(x = Risk, y = Return)) +
  geom_point(color = "blue", alpha = 0.5) +
  geom_point(aes(x = port_risk[best_index], y = port_returns[best_index]),
             color = "red", size = 4) +
  labs(title = "Efficient Frontier with Optimal Portfolio Highlighted",
       x = "Risk (Std Dev)",
       y = "Expected Return") +
  theme_minimal()
## Warning in geom_point(aes(x = port_risk[best_index], y = port_returns[best_index]), : All aesthetics have length 1, but the data has 5000 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.

# Bobot portofolio optimal
cat("Bobot Optimal Portofolio:\n")
## Bobot Optimal Portofolio:
names(best_weights) <- stocks
print(round(best_weights, 4))
## BBRI.JK TBIG.JK WIKA.JK 
##  0.0046  0.0059  0.9895
# Expected Return dan Risk dari portofolio optimal
cat("\nExpected Return Optimal Portfolio: ", round(port_returns[best_index], 5), "\n")
## 
## Expected Return Optimal Portfolio:  -0.00132
cat("Risk (Standard Deviation) Optimal Portfolio: ", round(port_risk[best_index], 5), "\n")
## Risk (Standard Deviation) Optimal Portfolio:  0.04055
cat("Sharpe Ratio Optimal Portfolio: ", round(sharpe_ratio[best_index], 5), "\n")
## Sharpe Ratio Optimal Portfolio:  -1.45055
#TUGAS WEEK 9
library(quantmod)
library(PerformanceAnalytics)
library(ggplot2)
stocks <- c("BBRI.JK", "WIKA.JK", "TBIG.JK")
getSymbols(stocks, src = "yahoo")
## Warning: WIKA.JK contains missing values. Some functions will not work if
## objects contain missing values in the middle of the series. Consider using
## na.omit(), na.approx(), na.fill(), etc to remove or replace them.
## [1] "BBRI.JK" "WIKA.JK" "TBIG.JK"
returns <- do.call(merge,
                   lapply(stocks,
                          function(x) dailyReturn(get(x))))
## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data
colnames(returns) <- stocks
returns <- returns["2022/2024"]
mu <- colMeans(returns)
cov_matrix <- var(returns)

weights <- solve(cov_matrix) %*% rep(1, 3)/(t(rep(1,3)) %*% solve(cov_matrix) %*% rep(1,3))[1,1]
weights
##               [,1]
## BBRI.JK 0.53487070
## WIKA.JK 0.04623837
## TBIG.JK 0.41889093
port_return <- sum(t(weights) %*% mu)
port_return
## [1] -0.0001226582
port_risk <- sum(t(weights) %*% cov_matrix %*% weights)
port_risk
## [1] 0.0001525291
#install.packages("quadprog")
library(quadprog)
## Warning: package 'quadprog' was built under R version 4.3.1
N <- nrow(cov_matrix)
Dmat <- cov_matrix
dvec <- rep(0, N)
Amat <- t(matrix(c(rep(1, N), diag(N)), nrow=N))
bvec <- c(1, rep(0, N))
meq <- 1
weights <- solve.QP(Dmat, dvec, t(Amat), bvec, meq)$solution
print(weights)
## [1] 0.53487070 0.04623837 0.41889093
mu_p <- port_return * 1.5
Amat <- t(matrix(c(rep(1, N), mu, diag(N)), nrow=N))
bvec <- c(1, mu_p, rep(0, N))
meq <- 2
weights <- solve.QP(Dmat, dvec, t(Amat), bvec, meq)$solution
print(weights)
## [1] 0.47377674 0.08226221 0.44396105
port_return2 <- sum(t(weights) %*% mu)
port_return2
## [1] -0.0001839873
port_risk2 <- sum(t(weights) %*% cov_matrix %*% weights)
port_risk2
## [1] 0.0001553938
# Plot Efficient Frontier versi highlight titik optimal
df <- data.frame(Risk = port_risk2, Return = port_return2)

ggplot(df, aes(x = Risk, y = Return)) +
  geom_point(color = "blue", alpha = 0.5) +
  geom_point(aes(x = port_risk[best_index], y = port_returns[best_index]),
             color = "red", size = 4) +
  labs(title = "Efficient Frontier with Optimal Portfolio Highlighted",
       x = "Risk (Std Dev)",
       y = "Expected Return") +
  theme_minimal()
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

#Berdasarkan hasil optimisasi portofolio terhadap saham BBRI.JK, TBIG.JK, dan WIKA.JK menggunakan pendekatan minimum variance dan quadratic programming, diperoleh bobot optimal berturut-turut sebesar 53,49%, 41,89%, dan 4,62%. Portofolio ini menghasilkan return ekspektasi harian sebesar -0,0123% dengan risiko (standar deviasi) sebesar 1,53%, yang menunjukkan bahwa meskipun portofolio ini efisien dari sisi risiko, namun tidak memberikan return positif. Simulasi peningkatan target return justru memperbesar risiko tanpa memperbaiki return secara signifikan. Hal ini menunjukkan bahwa komposisi saham yang digunakan belum ideal secara historis untuk membentuk portofolio yang menguntungkan, sehingga pemilihan aset yang lebih tepat atau evaluasi ulang terhadap periode data sangat disarankan.