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 (NVIDIA Corporation), MRNA (Moderna, Inc.) y XOM (Exxon Mobil Corporation).
# 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
## Pesos del portafolio:
## [1] 0.13420749 0.09196873 0.77382378
## Retorno esperado del portafolio: 0.0002554927
## Riesgo del portafolio: 0.01483831
## Índice de Sharpe: -0.4882301
## Precios simulados:
## [,1] [,2] [,3]
## [1,] 94.3100 25.1100006 104.3400
## [2,] 114.7260 14.9038832 100.6245
## [3,] 98.9609 7.8855076 105.6735
## [4,] 116.0803 5.4233713 107.0430
## [5,] 111.1301 5.8438646 126.8487
## [6,] 153.5475 3.9393276 143.2235
## [7,] 228.3950 2.2251739 151.5214
## [8,] 183.0151 1.0878777 148.7796
## [9,] 222.9844 0.9435394 134.9343
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 = "navy") +
labs(title = "Simulación de 1000 trayectorias de MGB para NVDA",
x = "Tiempo (años)",
y = "Precio Simulado") +
theme_minimal()
library(ggplot2)
# 1. Parámetros NECESARIOS (ajusta estos valores según tus datos)
T <- 1 # Horizonte temporal en años (ej: 1 año)
steps <- 252 # Número de pasos (días hábiles en 1 año)
n_sim <- 1000 # Número de simulaciones
S0 <- 100 # Precio actual de MRNA (¡cámbialo por el real!)
mu <- 0.05 # Rendimiento anualizado esperado (5%)
sigma <- 0.30 # Volatilidad anualizada (30%)
# 2. Generar simulaciones para MRNA (Modelo Geométrico Browniano)
set.seed(123) # Para reproducibilidad
simulations_mrna <- matrix(NA, nrow = steps + 1, ncol = n_sim)
simulations_mrna[1,] <- S0
for (i in 2:(steps + 1)) {
simulations_mrna[i,] <- simulations_mrna[i-1,] * exp(
(mu - 0.5 * sigma^2) * (T/steps) +
sigma * sqrt(T/steps) * rnorm(n_sim)
)
}
# 3. Crear el gráfico (ahora SÍ funcionará)
time <- seq(0, T, length.out = steps + 1)
sim_data <- data.frame(
Time = rep(time, n_sim),
Price = as.vector(simulations_mrna),
Simulation = rep(1:n_sim, each = steps + 1)
)
ggplot(sim_data, aes(x = Time, y = Price, group = Simulation)) +
geom_line(alpha = 0.1, color = "navy") +
labs(
title = "Simulación de 1000 trayectorias de MGB para MRNA",
x = "Tiempo (años)",
y = "Precio Simulado (USD)"
) +
theme_minimal() +
geom_hline(yintercept = S0, color = "red", linetype = "dashed") # Línea del precio inicial
library(ggplot2)
# 1. Parámetros base (¡AJUSTA ESTOS VALORES CON TUS DATOS REALES!)
T <- 1 # Horizonte temporal en años (1 año)
steps <- 252 # Número de pasos (días hábiles)
n_sim <- 1000 # Número de simulaciones
S0 <- 102.50 # Precio actual de XOM (cámbialo)
mu <- 0.08 # Rendimiento anualizado esperado (8%)
sigma <- 0.22 # Volatilidad anualizada (22%)
# 2. Generar simulaciones para XOM (Modelo Geométrico Browniano)
set.seed(123) # Fijar semilla para reproducibilidad
simulations_xom <- matrix(S0, nrow = steps + 1, ncol = n_sim)
for (i in 2:(steps + 1)) {
simulations_xom[i,] <- simulations_xom[i-1,] * exp(
(mu - 0.5 * sigma^2) * (T/steps) +
sigma * sqrt(T/steps) * rnorm(n_sim)
)
}
# 3. Preparar datos para el gráfico
time <- seq(0, T, length.out = steps + 1)
sim_data <- data.frame(
Time = rep(time, n_sim),
Price = as.vector(simulations_xom),
Simulation = rep(1:n_sim, each = steps + 1)
)
# 4. Visualización (estilo energía/oil)
ggplot(sim_data, aes(x = Time, y = Price, group = Simulation)) +
geom_line(alpha = 0.1, color = "navy") + # Azul petrolero
geom_hline(yintercept = S0, color = "red", linetype = "dashed", linewidth = 1) +
labs(
title = "Simulación de 1000 trayectorias de MGB para XOM",
x = "Tiempo (años)",
y = "Precio Simulado (USD)"
)
## Warning: Ignoring unknown parameters: linewidth
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 Sharpe calculado previamente fue \(−0.488\), lo que indica que el portafolio no está generando un retorno suficiente para compensar el riesgo asumido. Este valor negativo sugiere que el rendimiento del portafolio es inferior a la tasa libre de riesgo, lo que podría requerir ajustes en la composición del portafolio para mejorar su eficiencia.
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.
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.03477455
cat("VaR al 5%:", VaR_5, "\n")
## VaR al 5%: 0.02466233
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:\)$ = 0.85 ,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:
## NVDA MRNA XOM
## [1] 114076.37 78173.42 657750.22
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)