1. Instalación y carga de paquetes necesarios

to_install <- c("quantmod", "PerformanceAnalytics", "tseries", "ggplot2", 
                "dplyr", "tidyverse", "lubridate", "tibble", "RQuantLib")
missing_pkgs <- to_install[!(to_install %in% installed.packages()[, "Package"])]
if(length(missing_pkgs)) install.packages(missing_pkgs)

lapply(to_install, library, character.only = TRUE)
## [[1]]
##  [1] "quantmod"  "TTR"       "xts"       "zoo"       "stats"     "graphics" 
##  [7] "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[2]]
##  [1] "PerformanceAnalytics" "quantmod"             "TTR"                 
##  [4] "xts"                  "zoo"                  "stats"               
##  [7] "graphics"             "grDevices"            "utils"               
## [10] "datasets"             "methods"              "base"                
## 
## [[3]]
##  [1] "tseries"              "PerformanceAnalytics" "quantmod"            
##  [4] "TTR"                  "xts"                  "zoo"                 
##  [7] "stats"                "graphics"             "grDevices"           
## [10] "utils"                "datasets"             "methods"             
## [13] "base"                
## 
## [[4]]
##  [1] "ggplot2"              "tseries"              "PerformanceAnalytics"
##  [4] "quantmod"             "TTR"                  "xts"                 
##  [7] "zoo"                  "stats"                "graphics"            
## [10] "grDevices"            "utils"                "datasets"            
## [13] "methods"              "base"                
## 
## [[5]]
##  [1] "dplyr"                "ggplot2"              "tseries"             
##  [4] "PerformanceAnalytics" "quantmod"             "TTR"                 
##  [7] "xts"                  "zoo"                  "stats"               
## [10] "graphics"             "grDevices"            "utils"               
## [13] "datasets"             "methods"              "base"                
## 
## [[6]]
##  [1] "lubridate"            "forcats"              "stringr"             
##  [4] "purrr"                "readr"                "tidyr"               
##  [7] "tibble"               "tidyverse"            "dplyr"               
## [10] "ggplot2"              "tseries"              "PerformanceAnalytics"
## [13] "quantmod"             "TTR"                  "xts"                 
## [16] "zoo"                  "stats"                "graphics"            
## [19] "grDevices"            "utils"                "datasets"            
## [22] "methods"              "base"                
## 
## [[7]]
##  [1] "lubridate"            "forcats"              "stringr"             
##  [4] "purrr"                "readr"                "tidyr"               
##  [7] "tibble"               "tidyverse"            "dplyr"               
## [10] "ggplot2"              "tseries"              "PerformanceAnalytics"
## [13] "quantmod"             "TTR"                  "xts"                 
## [16] "zoo"                  "stats"                "graphics"            
## [19] "grDevices"            "utils"                "datasets"            
## [22] "methods"              "base"                
## 
## [[8]]
##  [1] "lubridate"            "forcats"              "stringr"             
##  [4] "purrr"                "readr"                "tidyr"               
##  [7] "tibble"               "tidyverse"            "dplyr"               
## [10] "ggplot2"              "tseries"              "PerformanceAnalytics"
## [13] "quantmod"             "TTR"                  "xts"                 
## [16] "zoo"                  "stats"                "graphics"            
## [19] "grDevices"            "utils"                "datasets"            
## [22] "methods"              "base"                
## 
## [[9]]
##  [1] "RQuantLib"            "lubridate"            "forcats"             
##  [4] "stringr"              "purrr"                "readr"               
##  [7] "tidyr"                "tibble"               "tidyverse"           
## [10] "dplyr"                "ggplot2"              "tseries"             
## [13] "PerformanceAnalytics" "quantmod"             "TTR"                 
## [16] "xts"                  "zoo"                  "stats"               
## [19] "graphics"             "grDevices"            "utils"               
## [22] "datasets"             "methods"              "base"

2. Descarga y preparación de datos

stocks <- c("RXRX", "MRNA", "BRPHF")
start_date <- as.Date("2022-06-01")
end_date <- as.Date("2025-03-31")

getSymbols(stocks, from = start_date, to = end_date, src = "yahoo")
## [1] "RXRX"  "MRNA"  "BRPHF"
prices <- do.call(merge, lapply(stocks, function(x) Cl(get(x))))
colnames(prices) <- stocks
returns <- na.omit(diff(log(prices)))
mu <- colMeans(returns)
cov_matrix <- cov(returns)

3. Simulación de Movimiento Geométrico Browniano (MGB)

set.seed(123)
sim_days <- as.numeric(end_date - index(prices)[nrow(prices)])
dt <- 1 / 252  
num_sims <- 10
sigma <- apply(returns, 2, sd)

MGB_sim <- array(NA, dim = c(sim_days, length(stocks), num_sims))
dimnames(MGB_sim) <- list(NULL, stocks, NULL)

for (sim in 1:num_sims) {
  for (i in 1:length(stocks)) {
    last_price <- as.numeric(prices[nrow(prices), i])
    S_t <- numeric(sim_days)
    S_t[1] <- last_price
    for (t in 2:sim_days) {
      Z_t <- rnorm(1)
      S_t[t] <- S_t[t-1] * exp((mu[i] - 0.5 * sigma[i]^2) * dt + sigma[i] * sqrt(dt) * Z_t)
    }
    MGB_sim[, i, sim] <- S_t
  }
}

4. Valuación de Opciones Europeas

S <- as.numeric(prices[nrow(prices), ])
K <- S  
T_exp <- 2
r <- 0.04
sigma_opt <- apply(returns, 2, sd)

european_calls <- sapply(1:length(stocks), function(i) {
  EuropeanOption("call", S[i], K[i], 0, r, T_exp, sigma_opt[i])$value
})

european_puts <- sapply(1:length(stocks), function(i) {
  EuropeanOption("put", S[i], K[i], 0, r, T_exp, sigma_opt[i])$value
})

5. Estrategia de Cobertura

inversion_total <- 1e6
porc_cobertura <- 0.85
monto_cobertura <- inversion_total * porc_cobertura
precios_opciones <- european_calls + european_puts
num_contratos <- monto_cobertura / sum(precios_opciones)

6. Frontera Eficiente y Carteras Aleatorias

set.seed(123)
n_sim <- 5000
pesos_random <- matrix(runif(n_sim * length(stocks)), ncol = length(stocks))
pesos_random <- t(apply(pesos_random, 1, function(x) x / sum(x)))
retornos_random <- pesos_random %*% mu
riesgo_random <- sqrt(rowSums((pesos_random %*% cov_matrix) * pesos_random))
sharpe_random <- retornos_random / riesgo_random
idx_tangency <- which.max(sharpe_random)
pesos_tangency <- pesos_random[idx_tangency, ]

7. Visualización de la Frontera Eficiente

ggplot() +
  geom_point(aes(x = riesgo_random, y = retornos_random), alpha = 0.3, color = "blue") +
  geom_point(aes(x = sqrt(t(pesos_tangency) %*% cov_matrix %*% pesos_tangency), 
                 y = sum(pesos_tangency * mu)), color = "red", size = 3) +
  labs(title = "Frontera Eficiente y Carteras Aleatorias",
       x = "Volatilidad", y = "Retorno Esperado") +
  theme_minimal()

8. Composición de Carteras Óptimas

w_min <- solve(cov_matrix) %*% rep(1, length(stocks))
w_min <- w_min / sum(w_min)
w_min_df <- data.frame(Accion = stocks, Peso = as.numeric(w_min))

w_tan_df <- data.frame(Accion = stocks, Peso = as.numeric(pesos_tangency))
ggplot(w_min_df, aes(x = Accion, y = Peso, fill = Accion)) +
  geom_bar(stat = "identity") +
  labs(title = "Pesos de la Cartera de Mínima Varianza", x = "Acción", y = "Peso") +
  scale_y_continuous(labels = scales::percent_format()) +
  theme_minimal()

ggplot(w_tan_df, aes(x = Accion, y = Peso, fill = Accion)) +
  geom_bar(stat = "identity") +
  labs(title = "Pesos de la Cartera de Tangencia (Máximo Sharpe)", x = "Acción", y = "Peso") +
  scale_y_continuous(labels = scales::percent_format()) +
  theme_minimal()

9. Comparación y análisis

cat("Pesos de la cartera mínima varianza:\n")
## Pesos de la cartera mínima varianza:
print(round(w_min, 4))
##         [,1]
## RXRX  0.1018
## MRNA  0.6525
## BRPHF 0.2457
sharpe_ratio_min <- mean(rowSums(returns %*% w_min)) / sd(rowSums(returns %*% w_min))
cat("\nSharpe Ratio cartera mínima varianza: ", round(sharpe_ratio_min, 4), "\n")
## 
## Sharpe Ratio cartera mínima varianza:  -0.035
sharpe_ratio_tan <- mean(rowSums(returns %*% pesos_tangency)) / sd(rowSums(returns %*% pesos_tangency))
cat("Sharpe Ratio cartera tangencia: ", round(sharpe_ratio_tan, 4), "\n")
## Sharpe Ratio cartera tangencia:  0.0178

10. Conclusiones y recomendaciones