Punto 1. Creación de un portafolio de media varianza

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

Porcentajes del portafolio

Los porcentajes asignados a las acciones son:

  • NVDA: 13.57%
  • MRNA: 8.82%
  • XOM: 77.61%

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

Punto 2. Análisis de criterios financieros y de riesgo

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%).

Índice de Sharpe

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.

Volatilidad

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.

Precios esperados trimestrales

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.

Comportamiento de mercado

El análisis de mercado sugiere que:

  • NVDA: Presenta alta volatilidad y señales de sobrecompra, lo que podría llevar a una corrección a la baja en el corto plazo. Sin embargo, los fundamentos a largo plazo son sólidos debido a su liderazgo en IA y tecnología de vehículos autónomos.
  • MRNA: Su comportamiento es más incierto, con menor liquidez y volatilidad más alta, lo que podría dificultar su estabilidad en el portafolio.
  • XOM: Beneficiada por la volatilidad en los precios del petróleo, pero expuesta a riesgos geopolíticos y fluctuaciones en la demanda energética.

Estructura de ganancias esperadas y pérdidas (VaR al 1% y 5%)

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.

Árboles binomiales

# 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

  • Las opciones call otorgan el derecho a comprar el activo subyacente a un precio fijo (strike) antes o en la fecha de vencimiento.
  • Los precios de las opciones call reflejan las expectativas de los inversores sobre el aumento del precio de las acciones.
    • NVDA (27.99): Este precio relativamente alto indica que los inversores esperan un aumento significativo en el precio de NVDA, lo que podría estar relacionado con su alta volatilidad (30%) y su potencial de crecimiento en sectores como la inteligencia artificial.
    • MRNA (18.33): El precio más bajo de la opción call de MRNA refleja una menor expectativa de crecimiento en comparación con NVDA, posiblemente debido a su mayor incertidumbre y menor peso en el portafolio.
    • XOM (23.09): El precio de la opción call de XOM es intermedio, lo que sugiere expectativas moderadas de aumento en el precio del petróleo y la energía.

Opciones Put

  • Las opciones put otorgan el derecho a vender el activo subyacente a un precio fijo antes o en la fecha de vencimiento.
  • Los precios de las opciones put reflejan las expectativas de los inversores sobre la caída del precio de las acciones.
    • NVDA (11.84): El precio de la opción put es menor que el de la call, lo que indica que los inversores tienen una menor probabilidad de que NVDA caiga significativamente.
    • MRNA (4.05): El precio bajo de la opción put de MRNA sugiere que los inversores no esperan una caída drástica en su precio, aunque su volatilidad es alta (50%).
    • XOM (11.59): El precio de la opción put de XOM es similar al de NVDA, lo que refleja una percepción de riesgo moderado en el sector energético.

Punto 3. Valuación de opciones y cobertura apalancada

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.

Cobertura del 85% de la inversión con apalancamiento

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.

Justificación de la estrategia

  • Cobertura del 85%: Este nivel de cobertura permite proteger la mayor parte del portafolio contra movimientos adversos, mientras se deja un margen para aprovechar posibles ganancias.
  • Apalancamiento: El uso de la tasa libre de riesgo reduce el costo de la cobertura, maximizando la eficiencia del capital.
  • Distribución proporcional: La asignación de fondos para la cobertura sigue los pesos del portafolio, asegurando que cada activo esté protegido en proporción a su contribución al riesgo y retorno.
# 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)