library(tidyquant)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## ── Attaching core tidyquant packages ─────────────────────── tidyquant 1.0.11 ──
## ✔ PerformanceAnalytics 2.0.8      ✔ TTR                  0.24.4
## ✔ quantmod             0.4.27     ✔ xts                  0.14.1
## ── Conflicts ────────────────────────────────────────── tidyquant_conflicts() ──
## ✖ zoo::as.Date()                 masks base::as.Date()
## ✖ zoo::as.Date.numeric()         masks base::as.Date.numeric()
## ✖ PerformanceAnalytics::legend() masks graphics::legend()
## ✖ quantmod::summary()            masks base::summary()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::first()  masks xts::first()
## ✖ dplyr::lag()    masks stats::lag()
## ✖ dplyr::last()   masks xts::last()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(quadprog)
tickers <- c("AAPL", "MSFT", "GOOG", "AMZN")
prices <- tq_get(tickers, from = "2020-01-01", to = "2022-01-01", get = "stock.prices") %>%
  group_by(symbol) %>%
  tq_transmute(select = adjusted, mutate_fun = periodReturn, period = "monthly", col_rename = "returns") %>%
  spread(symbol, returns)
head(prices)
## # A tibble: 6 × 5
##   date          AAPL    AMZN    GOOG    MSFT
##   <date>       <dbl>   <dbl>   <dbl>   <dbl>
## 1 2020-01-31  0.0305  0.0583  0.0489  0.0598
## 2 2020-02-28 -0.115  -0.0622 -0.0662 -0.0457
## 3 2020-03-31 -0.0698  0.0350 -0.132  -0.0265
## 4 2020-04-30  0.155   0.269   0.160   0.136 
## 5 2020-05-29  0.0851 -0.0128  0.0595  0.0254
## 6 2020-06-30  0.147   0.130  -0.0107  0.111
returns_matrix <- na.omit(as.matrix(prices[,-1]))
cov_matrix <- cov(returns_matrix)

Dmat <- 2 * cov_matrix
dvec <- rep(0, ncol(returns_matrix))
Amat <- cbind(rep(1, ncol(returns_matrix))) # sum of weights = 1
bvec <- 1
sol <- solve.QP(Dmat, dvec, Amat, bvec, meq = 1)

weights <- sol$solution
names(weights) <- colnames(returns_matrix)
weights
##       AAPL       AMZN       GOOG       MSFT 
## -0.1376707  0.1776578  0.1597203  0.8002926