Load data
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.3
##
## 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
library(xts)
getSymbols("FPT.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: FPT.VN 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] "FPT.VN"
getSymbols("SSI.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: SSI.VN 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] "SSI.VN"
getSymbols("HPG.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: HPG.VN 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] "HPG.VN"
getSymbols("VCB.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: VCB.VN 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] "VCB.VN"
getSymbols("BID.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: BID.VN 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] "BID.VN"
vcb <- to.weekly(VCB.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
hpg <- to.weekly(HPG.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
fpt <- to.weekly(FPT.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
ssi <- to.weekly(SSI.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
bid <- to.weekly(BID.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
data <- data.frame(
Date = index(vcb),
VCB = vcb$VCB.VN.Close,
hpg = hpg$HPG.VN.Close,
fpt = fpt$FPT.VN.Close,
ssi = ssi$SSI.VN.Close,
bid = bid$BID.VN.Close
)
data <- xts(data[,-1],order.by = data$Date)
library(PerformanceAnalytics)
## Warning: package 'PerformanceAnalytics' was built under R version 4.3.3
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
data_rt <- CalculateReturns(data, method = "log")[-1,]
head(data_rt)
## VCB.VN.Close HPG.VN.Close FPT.VN.Close SSI.VN.Close BID.VN.Close
## 2015-01-16 -0.016393785 -0.01869216 0.004132261 0.007299340 0.110862569
## 2015-01-23 0.021799196 -0.02871005 -0.002063996 0.000000000 0.071459007
## 2015-01-30 -0.038466223 -0.07455617 -0.018770117 -0.044617120 0.005730662
## 2015-02-06 0.008368196 -0.03838427 -0.004219440 -0.015325959 -0.005730662
## 2015-02-13 0.013793342 0.06725225 0.014690801 0.045290814 0.022728201
## 2015-02-27 0.050745241 -0.04362061 0.016529303 0.007352925 0.043963120
Giải bài toán với lợi nhuận kỳ vọng là 0.03%
library(quadprog)
# Hàm giải bài toán Frontier Portfolio
frontier_portfolio <- function(expected_returns, cov_matrix, target_return) {
n <- length(expected_returns)
# Ma trận D cho hàm mục tiêu (1/2)w'Vw
Dmat <- cov_matrix
# Vector d cho hàm mục tiêu (ở đây bằng 0)
dvec <- rep(0, n)
# Ràng buộc:
# 1. Tổng trọng số bằng 1
# 2. Lợi nhuận kỳ vọng bằng target_return
# 3. Tất cả trọng số >= 0 (nếu muốn không cho phép bán khống)
Amat <- cbind(
rep(1, n), # Ràng buộc tổng trọng số
expected_returns, # Ràng buộc lợi nhuận
diag(n) # Ràng buộc không âm (tùy chọn)
)
bvec <- c(
1, # Tổng trọng số bằng 1
target_return, # Lợi nhuận mục tiêu
rep(0, n) # Trọng số >= 0 (tùy chọn)
)
# Giải bài toán
solution <- solve.QP(
Dmat = Dmat,
dvec = dvec,
Amat = Amat,
bvec = bvec,
meq = 2 # 2 ràng buộc đầu là đẳng thức
)
# Trả về trọng số tối ưu
weights <- solution$solution
names(weights) <- names(expected_returns)
return(weights)
}
# Tính toán danh mục với lợi nhuận mục tiêu 0.3%
target_return <- 0.003
optimal_weights <- frontier_portfolio(er, covar, target_return)
# Kết quả
print("Trọng số tối ưu:")
## [1] "Trọng số tối ưu:"
print(optimal_weights)
## VCB.VN.Close HPG.VN.Close FPT.VN.Close SSI.VN.Close BID.VN.Close
## 0.43291596 0.11444026 0.14292827 0.25930996 0.05040554
# Kiểm tra ràng buộc
print(paste("Tổng trọng số:", sum(optimal_weights)))
## [1] "Tổng trọng số: 1"
print(paste("Lợi nhuận kỳ vọng:", sum(optimal_weights * er)))
## [1] "Lợi nhuận kỳ vọng: 0.003"
print(paste("Phương sai danh mục:", t(optimal_weights) %*% covar%*% optimal_weights))
## [1] "Phương sai danh mục: 0.00110179506905096"