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
- La cartera de tangencia presenta un mayor Sharpe ratio, lo que
indica mejor relación entre retorno esperado y riesgo.
- La cobertura con opciones distribuye eficientemente el 85% del
capital total, utilizando tanto calls como puts.
- Es recomendable diversificar entre ambas estrategias según el perfil
de riesgo del inversionista.