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)
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
library(quadprog)

# Tải dữ liệu từ Yahoo Finance
getSymbols("HSG.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: HSG.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] "HSG.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("PNJ.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: PNJ.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] "PNJ.VN"
getSymbols("MBB.VN", src = "yahoo", from = "2015-01-01", to = "2025-01-01")
## Warning: MBB.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] "MBB.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"
# Chuyển đổi dữ liệu thành dạng weekly
vcb <- to.weekly(VCB.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
HSG <- to.weekly(HSG.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
PNJ <- to.weekly(PNJ.VN)
## Warning in to.period(x, "weeks", name = name, ...): missing values removed from
## data
MBB <- to.weekly(MBB.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
# Kết hợp dữ liệu từ tất cả các cổ phiếu và chỉ giữ lại ngày chung
data_xts <- merge.xts(vcb$VCB.VN.Close, HSG$HSG.VN.Close, PNJ$PNJ.VN.Close, MBB$MBB.VN.Close, bid$BID.VN.Close)

# Đổi tên cột cho dễ hiểu
colnames(data_xts) <- c("VCB", "HSG", "PNJ", "MBB", "BID")

# Loại bỏ các dòng có NA (dữ liệu thiếu)
data_xts <- na.omit(data_xts)

# Tính toán lợi suất logarit
data_rt <- CalculateReturns(data_xts, method = "log")[-1,]

# Tính ma trận hiệp phương sai và kỳ vọng
covar <- cov(data_rt)
er <- sapply(data_rt, mean)

# 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           HSG           PNJ           MBB           BID 
##  1.339461e-01  0.000000e+00  3.432802e-01  5.227737e-01 -2.775558e-17
# 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.000615830225527991"
html_content <- "<html><body><h1>Xin chào, Thế giới!</h1></body></html>"

# Lưu nội dung HTML vào tệp
writeLines(html_content, con = "output.html")