A continuación, se detalla cómo resolver el primer literal del taller utilizando R para crear un portafolio de media varianza con las acciones NVDA, MRNA y XOM. Este portafolio será el subyacente para el análisis posterior.
# 1. Descargar datos históricos
symbols <- c("NVDA", "MRNA", "XOM")
start_date <- as.Date("2022-06-01")
end_date <- Sys.Date()
getSymbols(symbols, src = "yahoo", from = start_date, to = end_date)
## [1] "NVDA" "MRNA" "XOM"
# Extraer precios de cierre ajustados
prices <- do.call(merge, lapply(symbols, function(sym) Ad(get(sym))))
colnames(prices) <- symbols
# Calcular rendimientos diarios
returns <- na.omit(diff(log(prices)))
# 2. Calcular matriz de covarianza
cov_matrix <- cov(returns)
# 3. Optimización del portafolio de media-varianza
# Supongamos un portafolio inicial de $1,000,000
initial_investment <- 1e6
risk_free_rate <- 0.03 / 4 # Tasa libre de riesgo trimestral
# Función para optimizar el portafolio
optimize_portfolio <- function(returns, risk_free_rate) {
mean_returns <- colMeans(returns)
cov_matrix <- cov(returns)
# Resolver el problema de optimización cuadrática
Dmat <- 2 * cov_matrix
dvec <- rep(0, ncol(returns))
Amat <- cbind(1, diag(ncol(returns)))
bvec <- c(1, rep(0, ncol(returns)))
result <- solve.QP(Dmat, dvec, Amat, bvec, meq = 1)
weights <- result$solution
portfolio_return <- sum(weights * mean_returns)
portfolio_risk <- sqrt(t(weights) %*% cov_matrix %*% weights)
sharpe_ratio <- (portfolio_return - risk_free_rate) / portfolio_risk
list(weights = weights, return = portfolio_return, risk = portfolio_risk, sharpe = sharpe_ratio)
}
# Optimizar el portafolio
portfolio <- optimize_portfolio(returns, risk_free_rate)
# Resultados del portafolio
cat("Pesos del portafolio:\n")
## Pesos del portafolio:
print(portfolio$weights)
## [1] 0.12704521 0.08953716 0.78341762
cat("Retorno esperado del portafolio:", portfolio$return, "\n")
## Retorno esperado del portafolio: 0.0002814538
cat("Riesgo del portafolio:", portfolio$risk, "\n")
## Riesgo del portafolio: 0.01521396
cat("Índice de Sharpe:", portfolio$sharpe, "\n")
## Índice de Sharpe: -0.4744687
# 4. Simulación de precios futuros con MGB
simulate_prices <- function(prices, mu, sigma, T, steps) {
dt <- T / steps
n <- ncol(prices)
simulations <- matrix(NA, nrow = steps + 1, ncol = n)
simulations[1, ] <- as.numeric(prices[nrow(prices), ])
for (i in 2:(steps + 1)) {
Z <- rnorm(n)
simulations[i, ] <- simulations[i - 1, ] * exp((mu - 0.5 * sigma^2) * dt + sigma * sqrt(dt) * Z)
}
return(simulations)
}
# Parámetros para la simulación
mu <- colMeans(returns) * 252 # Retorno anualizado
sigma <- apply(returns, 2, sd) * sqrt(252) # Volatilidad anualizada
T <- 2 # Dos años
steps <- 8 # Trimestres
simulated_prices <- simulate_prices(prices, mu, sigma, T, steps)
# Mostrar precios simulados
cat("Precios simulados:\n")
## Precios simulados:
print(simulated_prices)
## [,1] [,2] [,3]
## [1,] 101.4900 24.715000 106.92000
## [2,] 128.2826 16.252931 105.68224
## [3,] 181.4054 9.477980 97.26414
## [4,] 242.3622 9.114441 78.39443
## [5,] 355.3992 3.938398 77.42198
## [6,] 378.8288 2.675888 81.03426
## [7,] 370.2416 1.975249 71.57964
## [8,] 370.9825 2.696318 75.57316
## [9,] 354.2880 1.941781 73.15937
Los porcentajes asignados a las acciones son:
Esto indica que el modelo de optimización de media-varianza ha asignado una mayor proporción del portafolio a XOM, probablemente debido a su menor riesgo relativo o mayor contribución al retorno ajustado al riesgo. Sin embargo, la alta concentración en XOM puede implicar una menor diversificación, lo que podría aumentar la exposición a riesgos específicos de este activo.
Retorno esperado del portafolio
El retorno esperado del portafolio es \(0.0368%\) diario. Este valor es bajo, lo que sugiere que el portafolio prioriza la minimización del riesgo sobre la maximización del retorno.
Riesgo del portafolio
El riesgo del portafolio, medido como la desviación estándar de los rendimientos, es 1.46% diario. Este nivel de riesgo es moderado, pero debe evaluarse en el contexto del bajo retorno esperado.
Índice de Sharpe
El índice de Sharpe es \(−0.488\), lo que indica que el portafolio no está generando un retorno suficiente para compensar el riesgo asumido. Un índice de Sharpe negativo sugiere que el rendimiento del portafolio es inferior a la tasa libre de riesgo.
Precios simulados
Los precios simulados muestran trayectorias futuras de las acciones bajo un modelo de Movimiento Geométrico Browniano (MGB). Se observa una alta volatilidad en las simulaciones, especialmente en XOM, que alcanza un precio de 298.83 en el último trimestre. Esto refuerza la importancia de evaluar el impacto de la volatilidad en la cobertura y el apalancamiento.
# Función para simular múltiples trayectorias de MGB
simulate_mgb <- function(S0, mu, sigma, T, steps, n_sim) {
dt <- T / steps
simulations <- matrix(NA, nrow = steps + 1, ncol = n_sim)
simulations[1, ] <- S0
for (i in 2:(steps + 1)) {
Z <- rnorm(n_sim)
simulations[i, ] <- simulations[i - 1, ] * exp((mu - 0.5 * sigma^2) * dt + sigma * sqrt(dt) * Z)
}
return(simulations)
}
# Parámetros para la simulación
S0 <- as.numeric(prices[nrow(prices), "NVDA"]) # Precio inicial de NVDA
mu_nvda <- mu["NVDA"] # Retorno anualizado de NVDA
sigma_nvda <- sigma["NVDA"] # Volatilidad anualizada de NVDA
T <- 2 # Dos años
steps <- 8 # Trimestres
n_sim <- 1000 # Número de trayectorias
# Simular trayectorias
simulations_nvda <- simulate_mgb(S0, mu_nvda, sigma_nvda, T, steps, n_sim)
# Graficar las trayectorias
library(ggplot2)
time <- seq(0, T, length.out = steps + 1)
sim_data <- data.frame(Time = rep(time, n_sim),
Price = as.vector(simulations_nvda),
Simulation = rep(1:n_sim, each = steps + 1))
ggplot(sim_data, aes(x = Time, y = Price, group = Simulation)) +
geom_line(alpha = 0.1, color = "blue") +
labs(title = "Simulación de 1000 trayectorias de MGB para NVDA",
x = "Tiempo (años)",
y = "Precio Simulado") +
theme_minimal()
El gráfico muestra 1000 trayectorias simuladas del precio de la acción de NVDA durante un período de 2 años. Se observa una alta dispersión en los precios simulados, con algunas trayectorias alcanzando valores superiores a 3000, mientras que otras permanecen cercanas al precio inicial o incluso disminuyen. Esto refleja la alta volatilidad de NVDA, lo que implica un mayor riesgo asociado a esta acción. Aunque existe un potencial significativo de crecimiento, la incertidumbre en los precios futuros es considerable, lo que puede impactar negativamente el rendimiento ajustado por riesgo del portafolio.
# simulación para MRNA
S0_mrna <- as.numeric(prices[nrow(prices), "MRNA"]) # Precio inicial de MRNA
mu_mrna <- mu["MRNA"] # Retorno anualizado de MRNA
sigma_mrna <- sigma["MRNA"] # Volatilidad anualizada de MRNA
# Simular trayectorias
simulations_mrna <- simulate_mgb(S0_mrna, mu_mrna, sigma_mrna, T, steps, n_sim)
# Graficar las trayectorias
sim_data_mrna <- data.frame(Time = rep(time, n_sim),
Price = as.vector(simulations_mrna),
Simulation = rep(1:n_sim, each = steps + 1))
ggplot(sim_data_mrna, aes(x = Time, y = Price, group = Simulation)) +
geom_line(alpha = 0.1, color = "red") +
labs(title = "Simulación de 1000 trayectorias de MGB para MRNA",
x = "Tiempo (años)",
y = "Precio Simulado") +
theme_minimal()
En el caso de MRNA, las trayectorias simuladas muestran una tendencia más moderada, con precios que oscilan entre 0 y 100. La dispersión es menor en comparación con NVDA, pero aún se evidencia una volatilidad significativa. Esto sugiere que MRNA también es un activo riesgoso, aunque con un menor rango de variación en los precios simulados. Este comportamiento puede ser indicativo de un activo con un perfil de riesgo-retorno menos extremo que NVDA, pero aún con incertidumbre considerable.
# Simulación para XOM
S0_xom <- as.numeric(prices[nrow(prices), "XOM"]) # Precio inicial de XOM
mu_xom <- mu["XOM"] # Retorno anualizado de XOM
sigma_xom <- sigma["XOM"] # Volatilidad anualizada de XOM
# Simular trayectorias
simulations_xom <- simulate_mgb(S0_xom, mu_xom, sigma_xom, T, steps, n_sim)
# Graficar las trayectorias
sim_data_xom <- data.frame(Time = rep(time, n_sim),
Price = as.vector(simulations_xom),
Simulation = rep(1:n_sim, each = steps + 1))
ggplot(sim_data_xom, aes(x = Time, y = Price, group = Simulation)) +
geom_line(alpha = 0.1, color = "green") +
labs(title = "Simulación de 1000 trayectorias de MGB para XOM",
x = "Tiempo (años)",
y = "Precio Simulado") +
theme_minimal()
El gráfico de XOM muestra una dispersión mucho menor en las trayectorias simuladas, con precios que oscilan entre 100 y 400. Esto refleja una menor volatilidad en comparación con NVDA y MRNA, lo que hace que XOM sea un activo más estable. Este comportamiento es típico de empresas del sector energético, que tienden a ser menos volátiles debido a la naturaleza más predecible de sus ingresos. XOM podría actuar como un estabilizador en el portafolio, compensando el riesgo asociado a las acciones más volátiles como NVDA y MRNA.
A continuación, se realiza el análisis solicitado para el portafolio compuesto por las acciones NVDA, MRNA y XOM, considerando los criterios de índice Sharpe, volatilidad, precios esperados trimestrales, comportamiento de mercado y estructura de ganancias esperadas y pérdidas (VaR al 1% y 5%).
El índice de Sharpe negativo (\(−0.488\)) indica que el portafolio no está generando un retorno suficiente para compensar el riesgo asumido, lo que significa que el rendimiento del portafolio es inferior a la tasa libre de riesgo. Este resultado es preocupante, ya que sugiere que el portafolio no está optimizado y que los inversionistas podrían estar mejor invirtiendo en activos libres de riesgo, como bonos gubernamentales. En este caso, la alta volatilidad de las acciones seleccionadas, especialmente NVDA y MRNA, podría estar contribuyendo al bajo desempeño ajustado por riesgo. Esto se refleja en la dispersión de los precios simulados para NVDA, donde algunas trayectorias alcanzan valores muy altos, pero muchas otras permanecen bajas, lo que genera un retorno promedio insuficiente.
El gráfico de simulación de 1000 trayectorias de Movimiento Geométrico Browniano (MGB) para NVDA refuerza esta interpretación. Aunque algunas trayectorias muestran un crecimiento significativo, la mayoría de los precios simulados permanecen cerca del precio inicial o incluso disminuyen, lo que refleja la alta volatilidad y el riesgo inherente de esta acción. Este comportamiento puede explicar por qué el portafolio, que incluye un peso significativo en NVDA, no logra superar la tasa libre de riesgo. Para mejorar el índice de Sharpe, sería necesario ajustar los pesos del portafolio, reduciendo la exposición a activos altamente volátiles como NVDA y MRNA, o diversificando con activos menos riesgosos, como XOM, que tiene un comportamiento más estable.
La volatilidad del portafolio (\(1.46\%\) diaria) refleja el nivel de riesgo asociado con las fluctuaciones de los rendimientos. Para convertirla a una base trimestral, se utiliza la fórmula:
\[ \sigma_{trimestral} = \sigma_{diaria} \times \sqrt{n} \] donde \(n\) es el número de días hábiles en un trimestre (aproximadamente 63). Por lo tanto, la volatilidad trimestral del portafolio es:
\[ \sigma_{trimestral} = 0.0146 \times \sqrt{63} \approx 0.1158 \text{ o } 11.58\% \]
Esto indica que el portafolio tiene una volatilidad moderada en un horizonte trimestral.
La volatilidad trimestral del 11.58% indica un nivel de riesgo moderado en un horizonte más amplio. Este cálculo es útil para evaluar el comportamiento del portafolio en períodos más largos y para tomar decisiones estratégicas de inversión, considerando tanto el riesgo como el retorno esperado. Una volatilidad moderada sugiere que el portafolio tiene un balance razonable entre activos volátiles y estables, aunque podría beneficiarse de ajustes para mejorar su rendimiento ajustado por riesgo.
La alta volatilidad de NVDA, evidenciada en las simulaciones, puede ser una oportunidad para inversionistas con alta tolerancia al riesgo, pero también representa un desafío para quienes buscan estabilidad.
Los precios esperados trimestrales se calculan utilizando la fórmula del MGB:
\[ S_t = S_0 \cdot e^{\left(\mu - \frac{\sigma^2}{2}\right)T + \sigma \sqrt{T}Z} \]
donde \(S_0\) es el precio inicial, \(\mu\) es el retorno esperado, \(\sigma\) es la volatilidad y \(Z\) es una variable aleatoria normal estándar. Para cada acción, se simulan 1000 trayectorias y se calcula el precio esperado al final del trimestre.
Los precios simulados previamente para cada trimestre ya reflejan estas expectativas. Por ejemplo, para NVDA, los precios esperados muestran un crecimiento significativo, pero con alta dispersión debido a la volatilidad.
El análisis de mercado sugiere que:
El Valor en Riesgo (VaR) se calcula utilizando la distribución normal de los rendimientos. Para un nivel de confianza del 1% y 5%, el VaR se puede calcular como:
\[ VaR_{\alpha} = \mu + z_{\alpha} \cdot \sigma \] donde \(z_{\alpha}\) es el valor crítico de la distribución normal estándar para el nivel de confianza deseado.
# Calcular VaR al 1% y 5%
alpha_1 <- 0.01
alpha_5 <- 0.05
z_1 <- qnorm(1 - alpha_1)
z_5 <- qnorm(1 - alpha_5)
VaR_1 <- portfolio$return + z_1 * portfolio$risk
VaR_5 <- portfolio$return + z_5 * portfolio$risk
cat("VaR al 1%:", VaR_1, "\n")
## VaR al 1%: 0.03567441
cat("VaR al 5%:", VaR_5, "\n")
## VaR al 5%: 0.02530619
El VaR al 5% y 1% proporciona una medida clara de las pérdidas potenciales, siendo más relevante el análisis trimestral para este caso.
# Función para construir el árbol binomial y valorar opciones
binomial_option <- function(S0, K, r, sigma, T, n, type = "call", american = TRUE) {
# Parámetros del árbol
dt <- T / n
u <- exp(sigma * sqrt(dt))
d <- 1 / u
p <- (exp(r * dt) - d) / (u - d)
# Construcción del árbol de precios
prices <- matrix(0, nrow = n + 1, ncol = n + 1)
for (i in 0:n) {
for (j in 0:i) {
prices[j + 1, i + 1] <- S0 * u^j * d^(i - j)
}
}
# Valoración de la opción en los nodos terminales
option_values <- matrix(0, nrow = n + 1, ncol = n + 1)
if (type == "call") {
option_values[, n + 1] <- pmax(0, prices[, n + 1] - K)
} else if (type == "put") {
option_values[, n + 1] <- pmax(0, K - prices[, n + 1])
}
# Retroceder en el árbol
for (i in (n - 1):0) {
for (j in 0:i) {
hold_value <- exp(-r * dt) * (p * option_values[j + 1, i + 2] + (1 - p) * option_values[j + 2, i + 2])
if (american) {
intrinsic_value <- if (type == "call") {
max(0, prices[j + 1, i + 1] - K)
} else {
max(0, K - prices[j + 1, i + 1])
}
option_values[j + 1, i + 1] <- max(hold_value, intrinsic_value)
} else {
option_values[j + 1, i + 1] <- hold_value
}
}
}
return(option_values[1, 1])
}
# Parámetros iniciales
S0 <- c(101.8, 25.73, 112.43) # Precios iniciales
K <- S0 # Precio de ejercicio
r <- 0.03 / 4 # Tasa libre de riesgo trimestral
sigma <- c(0.3, 0.5, 0.25) # Volatilidades
T <- 2 # Tiempo al vencimiento (años)
n <- 8 # Pasos (trimestres)
# Valuación de opciones americanas
call_prices <- sapply(1:3, function(i) binomial_option(S0[i], K[i], r, sigma[i], T, n, type = "call", american = TRUE))
put_prices <- sapply(1:3, function(i) binomial_option(S0[i], K[i], r, sigma[i], T, n, type = "put", american = TRUE))
cat("Precios de opciones americanas (Call):", call_prices, "\n")
## Precios de opciones americanas (Call): 27.98659 18.33135 23.08718
cat("Precios de opciones americanas (Put):", put_prices, "\n")
## Precios de opciones americanas (Put): 11.83992 4.046368 11.59163
Opciones Call
Opciones Put
A continuación, se detalla cómo realizar la valuación de opciones europeas y americanas (call y put) para las acciones NVDA, MRNA y XOM, y cómo cubrir el 85% de la inversión con apalancamiento utilizando la tasa de bonos del Tesoro. También se explica cómo dividir el dinero para la cobertura y justificar la estrategia.
El portafolio tiene un valor inicial de $1,000,000. Se cubrirá el 85% de esta inversión, es decir:
\[ \text{Cobertura} = 0.85 \times 1,000,000 = 850,000 \]
Apalancamiento
El apalancamiento se realiza utilizando la tasa libre de riesgo (\(r=0.0075\)) para financiar parte de la cobertura. Esto implica que se puede controlar una mayor exposición con una inversión inicial menor.
Distribución del dinero para la cobertura
El monto a cubrir se distribuye proporcionalmente según los pesos del portafolio (\(w=[0.1357, 0.0882, 0.7761]\)): \[ \text{Inversión}_{i} = \text{Cobertura} \times w_{i} \]
# Distribución del dinero para la cobertura
weights <- portfolio$weights
coverage <- 0.85 * initial_investment
investment_distribution <- coverage * weights
cat("Distribución del dinero para la cobertura:\n")
## Distribución del dinero para la cobertura:
cat(symbols, "\n")
## NVDA MRNA XOM
print(investment_distribution)
## [1] 107988.43 76106.59 665904.98
Compra de opciones
Se utilizarán opciones call y put para cubrir las posiciones. La cantidad de contratos se calcula dividiendo el monto asignado a cada acción entre el precio de la opción correspondiente.
# Función para construir y graficar un árbol binomial
graficar_arbol_binomial <- function(S0, u, d, n) {
# Crear una matriz para almacenar los precios en cada nodo
precios <- matrix(NA, nrow = n + 1, ncol = n + 1)
precios[1, 1] <- S0
# Calcular los precios en cada nodo
for (i in 1:n) {
for (j in 0:i) {
precios[j + 1, i + 1] <- S0 * u^j * d^(i - j)
}
}
# Graficar el árbol binomial
plot(0, 0, type = "n", xlim = c(0, n), ylim = c(0, n), xlab = "Pasos", ylab = "Niveles",
main = "Árbol Binomial de Precios", xaxt = "n", yaxt = "n")
# Dibujar los nodos y las conexiones
for (i in 1:n) {
for (j in 0:(i - 1)) {
x1 <- i - 1
y1 <- j
x2 <- i
y2_up <- j + 1
y2_down <- j
# Conexiones hacia arriba y hacia abajo
lines(c(x1, x2), c(y1, y2_up), col = "blue")
lines(c(x1, x2), c(y1, y2_down), col = "blue")
}
}
# Dibujar los nodos
for (i in 0:n) {
for (j in 0:i) {
points(i, j, pch = 19, col = "red")
text(i, j, round(precios[j + 1, i + 1], 2), pos = 4, cex = 0.7)
}
}
}
# Parámetros iniciales
S0 <- 100 # Precio inicial del activo
u <- 1.1 # Factor de subida
d <- 0.9 # Factor de bajada
n <- 4 # Número de pasos
# Llamar a la función para graficar el árbol binomial
graficar_arbol_binomial(S0, u, d, n)
El gráfico del árbol binomial muestra las posibles trayectorias del precio de un activo subyacente a lo largo de varios pasos discretos. Cada nodo (punto rojo) representa un precio posible del activo en un momento específico, calculado en función de los factores de subida (\(\mu\)) y bajada (\(\delta\)). Las líneas azules conectan los nodos, indicando las transiciones posibles entre precios en cada paso. Por ejemplo, desde un precio inicial de 100, el activo puede subir o bajar en cada paso, generando una estructura ramificada que refleja la incertidumbre del mercado. En este caso, el árbol tiene 4 pasos, y los precios finales oscilan entre 65 (mínimo) y 146.41 (máximo), lo que refleja la volatilidad del activo.