Primera parte

Para resolver este primer punto, nos enfocaremos en crear un portafolio de media-varianza con AMZN, DIS y PFE, y luego simular los precios futuros.

Primero, necesitamos obtener los datos históricos desde el 01/06/2022 y crear el portafolio óptimo. Luego realizaremos la simulación mediante el Movimiento Browniano Geométrico (MGB).

library(quantmod)
## Cargando paquete requerido: xts
## Cargando paquete requerido: zoo
## 
## Adjuntando el paquete: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## Cargando paquete requerido: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(ggplot2)
library(reshape2)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ lubridate 1.9.4     ✔ tibble    3.2.1
## ✔ purrr     1.0.4     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::first()  masks xts::first()
## ✖ dplyr::lag()    masks stats::lag()
## ✖ dplyr::last()   masks xts::last()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(scales)
## 
## Adjuntando el paquete: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
library(DiagrammeR)
## [1] "AMZN" "DIS"  "PFE"
## 
## Pesos óptimos del portafolio:
## [1] 15.85 25.72 58.42
## 
## Cantidad de acciones a comprar:
##  AMZN   DIS   PFE 
##   927  3080 25434
## 
## Estadísticas del portafolio:
## Retorno esperado anual: -10.84 %
## Riesgo anual: 19.54 %

Composición del Portafolio

La optimización de media-varianza nos ha dado un portafolio con la siguiente distribución:

  • PFE (Pfizer): 57.15% del portafolio
    • Es el activo con mayor peso
    • Representa 23,136 acciones
    • Sugiere que Pfizer se considera el activo más estable o con mejor relación riesgo-retorno
  • DIS (Disney): 26.86% del portafolio
    • Peso intermedio
    • Representa 2,744 acciones
    • Segunda posición en la asignación de capital
  • AMZN (Amazon): 15.99% del portafolio
    • Menor peso en el portafolio
    • Representa 816 acciones
    • La menor asignación probablemente debido a su mayor volatilidad o menor atractivo en términos de riesgo-retorno

Métricas de Rendimiento

  • Retorno Esperado Anual: -7%
    • Este retorno negativo es preocupante

    • Refleja el comportamiento bajista reciente de estos activos

    • Sugiere que podríamos estar en un período desafiante del mercado

    • Es importante notar que este es un estimado basado en datos históricos y no garantiza resultados futuros

  • Riesgo Anual: 19.1%
    • Representa la volatilidad esperada del portafolio
    • Es un nivel de riesgo moderado-alto
    • Significa que el portafolio podría fluctuar ±19.1% en un año con un nivel de confianza del 68% (una desviación estándar)

Implicaciones y Recomendaciones

  1. Consideraciones sobre el Retorno Negativo:
  • El retorno esperado negativo sugiere que podría ser necesario:
    • Reconsiderar la selección de activos
    • Esperar un mejor momento de entrada al mercado
    • Explorar estrategias de cobertura (que abordaremos en los siguientes puntos del taller)
  1. Balance Riesgo-Retorno:
  • Un riesgo de 19.1% para un retorno esperado negativo no es una relación óptima
  • El algoritmo ha intentado minimizar el riesgo dado el universo de activos disponible
  • La alta concentración en PFE sugiere que es el activo con mejor perfil de riesgo-retorno entre los tres
  1. Distribución de la Inversión:
  • El millón de dólares se distribuiría así:
    • PFE: ~$571,500
    • DIS: ~$268,600
    • AMZN: ~$159,900
  1. Consideraciones Prácticas:
  • La cantidad de acciones es realista y ejecutable en el mercado
  • La liquidez de estos títulos permite manejar estos volúmenes
  • Los costos de transacción deberían ser manejables dado el tamaño de las posiciones

Podemos decir según lo expresado anteriormente que este portafolio, aunque matemáticamente optimizado, presenta algunos desafíos:

  • El retorno esperado negativo sugiere la necesidad de estrategias de cobertura
  • La alta concentración en PFE podría requerir monitoreo especial
  • El nivel de riesgo es considerable para un retorno esperado negativo

Para el siguiente paso del taller (cobertura con opciones), estos resultados sugieren que será crucial desarrollar una estrategia de protección efectiva, especialmente considerando el retorno esperado negativo y el nivel de riesgo moderado-alto.

Segunda parte

Para esta segunda parte, se plantea la siguiente solución:

# Continuando con los datos anteriores...
library(PerformanceAnalytics)
## 
## Adjuntando el paquete: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
## 
##     legend
library(rugarch)
## Cargando paquete requerido: parallel
## 
## Adjuntando el paquete: 'rugarch'
## The following object is masked from 'package:purrr':
## 
##     reduce
## The following object is masked from 'package:stats':
## 
##     sigma
library(forecast)

# 1. Cálculo del Índice de Sharpe
rf_rate <- 0.0525  # 5.25% actual aproximado

# Función para calcular Sharpe Ratio del portafolio
calcular_sharpe <- function(retornos, pesos, rf) {
    ret_port <- sum(colMeans(retornos) * pesos) * 252
    vol_port <- sqrt(t(pesos) %*% cov(retornos) %*% pesos) * sqrt(252)
    sharpe <- (ret_port - rf) / vol_port
    return(sharpe)
}

# 2. Análisis de volatilidad y precios trimestrales
# Ajustamos el cálculo de precios trimestrales
dias_por_trimestre <- 63  # aproximadamente 63 días de trading por trimestre
n_trimestres <- 8  # 2 años

# Crear matriz para almacenar precios trimestrales
precios_trim <- matrix(NA, nrow = n_trimestres + 1, ncol = length(symbols))
colnames(precios_trim) <- symbols
rownames(precios_trim) <- paste0("Trimestre_", 0:n_trimestres)

# Calcular precios trimestrales promedio
for(i in 1:length(symbols)) {
    # Obtener índices trimestrales
    indices_trim <- seq(1, dias_simulacion + 1, by = dias_por_trimestre)
    
    # Para cada trimestre, calcular el precio promedio de todas las simulaciones
    for(t in 1:length(indices_trim)) {
        if(t <= nrow(precios_trim)) {
            precios_trim[t,i] <- mean(simulaciones[[symbols[i]]][indices_trim[t],])
        }
    }
}

# 3. Cálculo de VaR histórico
ret_port_hist <- as.numeric(retornos %*% pesos)
var_1pct <- quantile(ret_port_hist, 0.01)
var_5pct <- quantile(ret_port_hist, 0.05)

# Imprimir resultados
cat("\n=== ANÁLISIS COMPLETO DEL PORTAFOLIO ===\n")
## 
## === ANÁLISIS COMPLETO DEL PORTAFOLIO ===
cat("\n1. MÉTRICAS DE RENDIMIENTO")
## 
## 1. MÉTRICAS DE RENDIMIENTO
cat("\nÍndice de Sharpe:", round(calcular_sharpe(retornos, pesos, rf_rate), 4))
## 
## Índice de Sharpe: -0.8232
cat("\nVolatilidad anualizada:", round(calc_riesgo(pesos) * 100, 2), "%")
## 
## Volatilidad anualizada: 19.54 %
cat("\n\n2. PRECIOS ESPERADOS TRIMESTRALES")
## 
## 
## 2. PRECIOS ESPERADOS TRIMESTRALES
fechas_trim <- format(seq(Sys.Date(), by = "quarter", length.out = n_trimestres + 1), "%Y-%m-%d")
precios_trim_df <- data.frame(Fecha = fechas_trim, precios_trim)
knitr::kable(precios_trim_df)
Fecha AMZN DIS PFE
Trimestre_0 2025-04-06 171.0000 83.53000 22.97000
Trimestre_1 2025-07-06 179.6402 82.10918 21.74331
Trimestre_2 2025-10-06 189.0396 80.97908 20.56527
Trimestre_3 2026-01-06 196.9690 79.17376 19.46983
Trimestre_4 2026-04-06 206.1259 78.61177 18.50458
Trimestre_5 2026-07-06 216.4343 77.25670 17.47595
Trimestre_6 2026-10-06 228.1237 76.51507 16.46466
Trimestre_7 2027-01-06 237.5524 76.59570 15.44982
Trimestre_8 2027-04-06 249.1478 75.84372 14.54272
cat("\n\n3. MEDIDAS DE RIESGO")
## 
## 
## 3. MEDIDAS DE RIESGO
cat("\nVaR diario al 1%:", round(var_1pct * 100, 4), "%")
## 
## VaR diario al 1%: -3.2775 %
cat("\nVaR diario al 5%:", round(var_5pct * 100, 4), "%")
## 
## VaR diario al 5%: -1.9995 %
# Visualizaciones
# 1. Gráfica de evolución de precios esperados
precios_trim_long <- reshape2::melt(precios_trim_df, id.vars = "Fecha")
g3 <- ggplot(precios_trim_long, aes(x = Fecha, y = value, color = variable, group = variable)) +
    geom_line() +
    geom_point() +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(title = "Precios Esperados Trimestrales",
         x = "Fecha",
         y = "Precio",
         color = "Activo") +
    scale_y_continuous(labels = scales::dollar_format())

# 2. Gráfica de distribución de retornos del portafolio
g4 <- ggplot(data.frame(retornos = ret_port_hist), aes(x = retornos)) +
    geom_histogram(aes(y = ..density..), bins = 30, fill = "skyblue", alpha = 0.7) +
    geom_density(color = "red") +
    geom_vline(xintercept = var_5pct, color = "orange", linetype = "dashed") +
    geom_vline(xintercept = var_1pct, color = "red", linetype = "dashed") +
    theme_minimal() +
    labs(title = "Distribución de Retornos del Portafolio",
         x = "Retornos",
         y = "Densidad") +
    annotate("text", x = var_5pct, y = 0, label = "VaR 5%", angle = 90, vjust = -0.5) +
    annotate("text", x = var_1pct, y = 0, label = "VaR 1%", angle = 90, vjust = -0.5)

# 3. Gráfica de volatilidad móvil
vol_movil <- rollapply(ret_port_hist, width = 21, FUN = sd) * sqrt(252) * 100
vol_df <- data.frame(
    Fecha = index(retornos)[(21):length(ret_port_hist)],
    Volatilidad = vol_movil
)

g5 <- ggplot(vol_df, aes(x = Fecha, y = Volatilidad)) +
    geom_line(color = "blue") +
    theme_minimal() +
    labs(title = "Volatilidad Móvil del Portafolio (21 días)",
         x = "Fecha",
         y = "Volatilidad Anualizada (%)") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Mostrar gráficas
print(g3)

print(g4)
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

print(g5)

# Análisis adicional: Correlaciones entre activos
cor_matrix <- cor(retornos)
cat("\n4. MATRIZ DE CORRELACIONES\n")
## 
## 4. MATRIZ DE CORRELACIONES
print(round(cor_matrix, 4))
##        AMZN    DIS    PFE
## AMZN 1.0000 0.4299 0.1450
## DIS  0.4299 1.0000 0.2021
## PFE  0.1450 0.2021 1.0000
# Estadísticas descriptivas del portafolio
cat("\n5. ESTADÍSTICAS DESCRIPTIVAS DEL PORTAFOLIO\n")
## 
## 5. ESTADÍSTICAS DESCRIPTIVAS DEL PORTAFOLIO
stats <- data.frame(
    Métrica = c("Media Anualizada", "Volatilidad Anualizada", "Sharpe Ratio",
                "VaR 1%", "VaR 5%", "Asimetría", "Curtosis"),
    Valor = c(
        mean(ret_port_hist) * 252 * 100,
        sd(ret_port_hist) * sqrt(252) * 100,
        calcular_sharpe(retornos, pesos, rf_rate),
        var_1pct * 100,
        var_5pct * 100,
        skewness(ret_port_hist),
        kurtosis(ret_port_hist)
    )
)
print(stats)
##                  Métrica       Valor
## 1       Media Anualizada -10.8391108
## 2 Volatilidad Anualizada  19.5441791
## 3           Sharpe Ratio  -0.8232175
## 4                 VaR 1%  -3.2775306
## 5                 VaR 5%  -1.9994907
## 6              Asimetría  -0.2434677
## 7               Curtosis   1.4083024

A continuación, se presentan los resultados del análisis completo del portafolio:

  1. Métricas de Rendimiento

Índice de Sharpe (-0.6411)

Volatilidad Anualizada (19.1%)

  1. Precios Esperados Trimestrales

Tendencias por Activo:

  1. Medidas de Riesgo

VaR (Value at Risk)

  1. Matriz de Correlaciones
  1. Estadísticas Descriptivas

Ajustes Inmediatos:

Estrategia de Cobertura:

Monitoreo y Rebalanceo:

Consideraciones de Riesgo: * El portafolio actual necesita mejoras significativas * La relación riesgo-retorno es desfavorable * Se requieren ajustes para mejorar el Índice de Sharpe

Tercera parte

A continuación planteamos la solución:

# Cargar bibliotecas necesarias
library(quantmod)
library(tidyverse)

# Función para calcular el precio de una opción europea usando Black-Scholes
bs_option <- function(S, K, r, T, sigma, type = "call") {
    d1 <- (log(S/K) + (r + sigma^2/2)*T) / (sigma*sqrt(T))
    d2 <- d1 - sigma*sqrt(T)
    
    if(type == "call") {
        price <- S*pnorm(d1) - K*exp(-r*T)*pnorm(d2)
    } else {
        price <- K*exp(-r*T)*pnorm(-d2) - S*pnorm(-d1)
    }
    return(price)
}

# Función para calcular opción americana usando el método binomial
american_option <- function(S, K, r, T, sigma, N = 100, type = "call") {
    dt <- T/N
    u <- exp(sigma*sqrt(dt))
    d <- 1/u
    p <- (exp(r*dt) - d)/(u - d)
    
    # Crear árbol de precios
    stock_prices <- matrix(0, N+1, N+1)
    option_values <- matrix(0, N+1, N+1)
    
    # Llenar árbol de precios
    for(i in 1:(N+1)) {
        for(j in 1:i) {
            stock_prices[i,j] <- S * u^(j-1) * d^((i-1)-(j-1))
        }
    }
    
    # Valores finales
    if(type == "call") {
        option_values[N+1,] <- pmax(stock_prices[N+1,] - K, 0)
    } else {
        option_values[N+1,] <- pmax(K - stock_prices[N+1,], 0)
    }
    
    # Retroceder en el árbol
    for(i in N:1) {
        for(j in 1:i) {
            hold_value <- exp(-r*dt)*(p*option_values[i+1,j+1] + (1-p)*option_values[i+1,j])
            if(type == "call") {
                exercise_value <- max(stock_prices[i,j] - K, 0)
            } else {
                exercise_value <- max(K - stock_prices[i,j], 0)
            }
            option_values[i,j] <- max(hold_value, exercise_value)
        }
    }
    
    return(option_values[1,1])
}

# Parámetros actuales
rf_rate <- 0.0525  # Tasa libre de riesgo (T-bond)
inversion_total <- 1000000
monto_cobertura <- inversion_total * 0.85  # 85% para cobertura
tiempo_trimestral <- 0.25  # 3 meses en años

# Calcular precios de opciones para cada activo
calcular_opciones <- function(S, sigma, K = S, T = tiempo_trimestral, r = rf_rate) {
    opciones <- list(
        europea_call = bs_option(S, K, r, T, sigma, "call"),
        europea_put = bs_option(S, K, r, T, sigma, "put"),
        americana_call = american_option(S, K, r, T, sigma, type = "call"),
        americana_put = american_option(S, K, r, T, sigma, type = "put")
    )
    return(opciones)
}

# Calcular opciones para cada activo
precios_opciones <- list()
for(i in 1:length(symbols)) {
    S <- precios_actuales[i]
    sigma <- volatilidades[i]
    precios_opciones[[symbols[i]]] <- calcular_opciones(S, sigma)
}

# Calcular distribución de cobertura basada en riesgo
riesgo_relativo <- volatilidades / sum(volatilidades)
distribucion_cobertura <- riesgo_relativo * monto_cobertura

# Imprimir resultados
cat("\n=== ANÁLISIS DE COBERTURA CON OPCIONES ===\n")
## 
## === ANÁLISIS DE COBERTURA CON OPCIONES ===
cat("\n1. PRECIOS DE OPCIONES (por contrato):\n")
## 
## 1. PRECIOS DE OPCIONES (por contrato):
for(sym in symbols) {
    cat("\nActivo:", sym)
    cat("\nOpción Europea Call:", round(precios_opciones[[sym]]$europea_call, 2))
    cat("\nOpción Europea Put:", round(precios_opciones[[sym]]$europea_put, 2))
    cat("\nOpción Americana Call:", round(precios_opciones[[sym]]$americana_call, 2))
    cat("\nOpción Americana Put:", round(precios_opciones[[sym]]$americana_put, 2))
}
## 
## Activo: AMZN
## Opción Europea Call: 13.05
## Opción Europea Put: 10.82
## Opción Americana Call: 13.02
## Opción Americana Put: 10.98
## Activo: DIS
## Opción Europea Call: 5.48
## Opción Europea Put: 4.39
## Opción Americana Call: 5.47
## Opción Americana Put: 4.48
## Activo: PFE
## Opción Europea Call: 1.23
## Opción Europea Put: 0.93
## Opción Americana Call: 1.23
## Opción Americana Put: 0.96
cat("\n\n2. DISTRIBUCIÓN DE COBERTURA:\n")
## 
## 
## 2. DISTRIBUCIÓN DE COBERTURA:
for(i in 1:length(symbols)) {
    cat("\n", symbols[i], ": $", round(distribucion_cobertura[i], 2),
        " (", round(riesgo_relativo[i] * 100, 2), "%)", sep="")
}
## 
## AMZN: $337497.5 (39.71%)
## DIS: $285610.8 (33.6%)
## PFE: $226891.7 (26.69%)
# Calcular número de contratos por tipo de opción
calcular_contratos <- function(monto, precio_opcion) {
    return(floor(monto / (precio_opcion * 100)))  # 100 acciones por contrato
}

# Estrategia de cobertura por activo
cat("\n\n3. ESTRATEGIA DE COBERTURA POR ACTIVO:\n")
## 
## 
## 3. ESTRATEGIA DE COBERTURA POR ACTIVO:
for(i in 1:length(symbols)) {
    monto_activo <- distribucion_cobertura[i]
    opciones <- precios_opciones[[symbols[i]]]
    
    # Dividir el monto entre puts y calls
    monto_put <- monto_activo * 0.6  # 60% para puts (protección)
    monto_call <- monto_activo * 0.4  # 40% para calls (upside)
    
    cat("\n", symbols[i], ":", sep="")
    cat("\n  - Puts Europeos:", calcular_contratos(monto_put * 0.5, opciones$europea_put), "contratos")
    cat("\n  - Puts Americanos:", calcular_contratos(monto_put * 0.5, opciones$americana_put), "contratos")
    cat("\n  - Calls Europeos:", calcular_contratos(monto_call * 0.5, opciones$europea_call), "contratos")
    cat("\n  - Calls Americanos:", calcular_contratos(monto_call * 0.5, opciones$americana_call), "contratos")
}
## 
## AMZN:
##   - Puts Europeos: 93 contratos
##   - Puts Americanos: 92 contratos
##   - Calls Europeos: 51 contratos
##   - Calls Americanos: 51 contratos
## DIS:
##   - Puts Europeos: 194 contratos
##   - Puts Americanos: 191 contratos
##   - Calls Europeos: 104 contratos
##   - Calls Americanos: 104 contratos
## PFE:
##   - Puts Europeos: 730 contratos
##   - Puts Americanos: 711 contratos
##   - Calls Europeos: 368 contratos
##   - Calls Americanos: 369 contratos
# Calcular costo total de la cobertura
cat("\n\n4. ANÁLISIS DE COSTOS Y APALANCAMIENTO:\n")
## 
## 
## 4. ANÁLISIS DE COSTOS Y APALANCAMIENTO:
costo_total <- 0
for(sym in symbols) {
    ops <- precios_opciones[[sym]]
    costo_total <- costo_total + sum(unlist(ops))
}

apalancamiento <- monto_cobertura / costo_total
cat("\nCosto total de opciones por contrato: $", round(costo_total, 2))
## 
## Costo total de opciones por contrato: $ 72.03
cat("\nRatio de apalancamiento:", round(apalancamiento, 2))
## 
## Ratio de apalancamiento: 11800.67

AMZN (Precio actual ≈ $196)

DIS (Precio actual ≈ $98) * Calls vs Puts: Diferencia moderada (calls $6.29 vs puts $5.02) * Refleja expectativa neutral-alcista * Europea vs Americana: * Diferencias mínimas * Valor de ejercicio temprano es marginal

PFE (Precio actual ≈ $25)

  1. Distribución de Cobertura

La distribución basada en riesgo muestra:

  1. Estrategia de Contratos

AMZN

DIS

PFE

  1. Análisis de Apalancamiento

Generación de árboles binomiales

# Función para crear y visualizar árbol binomial
crear_arbol_binomial <- function(S0, K, r, T, sigma, n, tipo = "call") {
    # Parámetros del árbol
    dt <- T/n
    u <- exp(sigma * sqrt(dt))
    d <- 1/u
    p <- (exp(r*dt) - d)/(u - d)
    
    # Crear matrices para precios y valores de opción
    precios <- matrix(0, nrow = n+1, ncol = n+1)
    valores <- matrix(0, nrow = n+1, ncol = n+1)
    
    # Llenar árbol de precios
    for(i in 1:(n+1)) {
        for(j in 1:i) {
            precios[i,j] <- S0 * u^(j-1) * d^((i-1)-(j-1))
        }
    }
    
    # Calcular valores de la opción en vencimiento
    if(tipo == "call") {
        valores[n+1,] <- pmax(precios[n+1,] - K, 0)
    } else {
        valores[n+1,] <- pmax(K - precios[n+1,], 0)
    }
    
    # Retroceder en el árbol
    for(i in n:1) {
        for(j in 1:i) {
            valor_mantenimiento <- exp(-r*dt) * (p * valores[i+1,j+1] + (1-p) * valores[i+1,j])
            if(tipo == "call") {
                valor_ejercicio <- max(precios[i,j] - K, 0)
            } else {
                valor_ejercicio <- max(K - precios[i,j], 0)
            }
            valores[i,j] <- max(valor_mantenimiento, valor_ejercicio)
        }
    }
    
    return(list(precios = precios, valores = valores, precio_opcion = valores[1,1]))
}

# Función para visualizar el árbol


visualizar_arbol <- function(matriz, n_pasos = 3, tipo = "precios") {
    # Crear nodos y conexiones para el gráfico
    nodes <- c()
    edges <- c()
    node_counter <- 1
    
    for(i in 1:(n_pasos+1)) {
        for(j in 1:i) {
            # Añadir nodo
            valor <- round(matriz[i,j], 2)
            nodes <- c(nodes, 
                      paste0(node_counter, "[label='", valor, "']"))
            
            # Añadir conexiones
            if(i < (n_pasos+1)) {
                # Conexión hacia arriba
                edges <- c(edges, 
                          paste0(node_counter, "->", node_counter + i))
                # Conexión hacia abajo
                if(j < i) {
                    edges <- c(edges, 
                             paste0(node_counter, "->", node_counter + i + 1))
                }
            }
            node_counter <- node_counter + 1
        }
    }
    
    # Crear gráfico
    graph <- paste0("digraph G {\n",
                   "  node [shape=circle]\n",
                   paste(nodes, collapse="\n"),
                   "\n",
                   paste(edges, collapse="\n"),
                   "\n}")
    
    grViz(graph)
}

# Ejemplo de uso para cada activo
# Usaremos 3 pasos para mejor visualización
n_pasos <- 3

# Crear y visualizar árboles para cada activo
for(sym in symbols) {
    cat("\nÁrboles Binomiales para", sym, "\n")
    
    # Parámetros específicos del activo
    S0 <- precios_actuales[which(symbols == sym)]
    sigma <- volatilidades[which(symbols == sym)]
    
    # Crear árboles para call y put
    arbol_call <- crear_arbol_binomial(S0, S0, rf_rate, tiempo_trimestral, 
                                     sigma, n_pasos, "call")
    arbol_put <- crear_arbol_binomial(S0, S0, rf_rate, tiempo_trimestral, 
                                    sigma, n_pasos, "put")
    
    cat("\nCall Option:")
    cat("\nPrecio de la opción:", round(arbol_call$precio_opcion, 2))
    cat("\nÁrbol de precios del subyacente:\n")
    print(round(arbol_call$precios[1:(n_pasos+1), 1:(n_pasos+1)], 2))
    cat("\nÁrbol de valores de la opción:\n")
    print(round(arbol_call$valores[1:(n_pasos+1), 1:(n_pasos+1)], 2))
    
    # Visualizar árboles
    cat("\nVisualización del árbol de precios:\n")
    visualizar_arbol(arbol_call$precios, n_pasos, "precios")
    cat("\nVisualización del árbol de valores de la opción:\n")
    visualizar_arbol(arbol_call$valores, n_pasos, "valores")
    
    cat("\nPut Option:")
    cat("\nPrecio de la opción:", round(arbol_put$precio_opcion, 2))
    cat("\nÁrbol de precios del subyacente:\n")
    print(round(arbol_put$precios[1:(n_pasos+1), 1:(n_pasos+1)], 2))
    cat("\nÁrbol de valores de la opción:\n")
    print(round(arbol_put$valores[1:(n_pasos+1), 1:(n_pasos+1)], 2))
    
    # Visualizar árboles
    cat("\nVisualización del árbol de valores de la opción:\n")
    visualizar_arbol(arbol_put$valores, n_pasos, "valores")
}
## 
## Árboles Binomiales para AMZN 
## 
## Call Option:
## Precio de la opción: 14.05
## Árbol de precios del subyacente:
##        [,1]   [,2]   [,3]   [,4]
## [1,] 171.00   0.00   0.00   0.00
## [2,] 154.50 189.26   0.00   0.00
## [3,] 139.59 171.00 209.48   0.00
## [4,] 126.12 154.50 189.26 231.85
## 
## Árbol de valores de la opción:
##       [,1]  [,2]  [,3]  [,4]
## [1,] 14.05  0.00  0.00  0.00
## [2,]  4.46 23.91  0.00  0.00
## [3,]  0.00  9.02 39.23  0.00
## [4,]  0.00  0.00 18.26 60.85
## 
## Visualización del árbol de precios:
## 
## Visualización del árbol de valores de la opción:
## 
## Put Option:
## Precio de la opción: 12
## Árbol de precios del subyacente:
##        [,1]   [,2]   [,3]   [,4]
## [1,] 171.00   0.00   0.00   0.00
## [2,] 154.50 189.26   0.00   0.00
## [3,] 139.59 171.00 209.48   0.00
## [4,] 126.12 154.50 189.26 231.85
## 
## Árbol de valores de la opción:
##       [,1]  [,2] [,3] [,4]
## [1,] 12.00  0.00    0    0
## [2,] 19.84  4.15    0    0
## [3,] 31.41  8.28    0    0
## [4,] 44.88 16.50    0    0
## 
## Visualización del árbol de valores de la opción:
## 
## Árboles Binomiales para DIS 
## 
## Call Option:
## Precio de la opción: 5.9
## Árbol de precios del subyacente:
##       [,1]  [,2]  [,3]   [,4]
## [1,] 83.53  0.00  0.00   0.00
## [2,] 76.66 91.02  0.00   0.00
## [3,] 70.35 83.53 99.18   0.00
## [4,] 64.56 76.66 91.02 108.08
## 
## Árbol de valores de la opción:
##      [,1] [,2]  [,3]  [,4]
## [1,] 5.90 0.00  0.00  0.00
## [2,] 1.89 9.89  0.00  0.00
## [3,] 0.00 3.76 16.02  0.00
## [4,] 0.00 0.00  7.49 24.55
## 
## Visualización del árbol de precios:
## 
## Visualización del árbol de valores de la opción:
## 
## Put Option:
## Precio de la opción: 4.9
## Árbol de precios del subyacente:
##       [,1]  [,2]  [,3]   [,4]
## [1,] 83.53  0.00  0.00   0.00
## [2,] 76.66 91.02  0.00   0.00
## [3,] 70.35 83.53 99.18   0.00
## [4,] 64.56 76.66 91.02 108.08
## 
## Árbol de valores de la opción:
##       [,1] [,2] [,3] [,4]
## [1,]  4.90 0.00    0    0
## [2,]  8.21 1.68    0    0
## [3,] 13.18 3.39    0    0
## [4,] 18.97 6.87    0    0
## 
## Visualización del árbol de valores de la opción:
## 
## Árboles Binomiales para PFE 
## 
## Call Option:
## Precio de la opción: 1.32
## Árbol de precios del subyacente:
##       [,1]  [,2]  [,3]  [,4]
## [1,] 22.97  0.00  0.00  0.00
## [2,] 21.46 24.59  0.00  0.00
## [3,] 20.04 22.97 26.33  0.00
## [4,] 18.72 21.46 24.59 28.19
## 
## Árbol de valores de la opción:
##      [,1] [,2] [,3] [,4]
## [1,] 1.32 0.00 0.00 0.00
## [2,] 0.43 2.18 0.00 0.00
## [3,] 0.00 0.83 3.46 0.00
## [4,] 0.00 0.00 1.62 5.22
## 
## Visualización del árbol de precios:
## 
## Visualización del árbol de valores de la opción:
## 
## Put Option:
## Precio de la opción: 1.05
## Árbol de precios del subyacente:
##       [,1]  [,2]  [,3]  [,4]
## [1,] 22.97  0.00  0.00  0.00
## [2,] 21.46 24.59  0.00  0.00
## [3,] 20.04 22.97 26.33  0.00
## [4,] 18.72 21.46 24.59 28.19
## 
## Árbol de valores de la opción:
##      [,1] [,2] [,3] [,4]
## [1,] 1.05 0.00    0    0
## [2,] 1.79 0.35    0    0
## [3,] 2.93 0.73    0    0
## [4,] 4.25 1.51    0    0
## 
## Visualización del árbol de valores de la opción:
  • Cada nodo muestra un posible precio futuro
  • Las ramificaciones muestran movimientos up/down
  • Los valores finales muestran el payoff de la opción
  • Los valores intermedios muestran decisiones de ejercicio