1 Resumen Ejecutivo

1.1 Introducción

Este documento presenta un análisis exhaustivo de cobertura de portafolio mediante opciones financieras, desarrollado conforme a los principios de gestión moderna de riesgos y valuación de derivados. El estudio abarca la construcción de un portafolio óptimo de $10,000,000 USD invertido en tres activos de alta capitalización sin pagos de dividendos relevantes (Taiwan Semiconductor, Costco Wholesale Corp. y Johnson & Johnson), seguido de una estrategia integral de cobertura mediante opciones europeas y americanas valuadas con el modelo binomial de Cox, Ross y Rubinstein (1979).

1.1.1 Marco Teórico

La teoría moderna de portafolios, iniciada por Markowitz (1952), establece que los inversionistas racionales buscan maximizar el retorno esperado para un nivel dado de riesgo, o equivalentemente, minimizar el riesgo para un retorno esperado determinado. En este contexto, Hull (2018) señala que las opciones financieras constituyen instrumentos fundamentales para la gestión de riesgos en portafolios de inversión, permitiendo establecer pisos de protección mediante estrategias de protective put.

1.1.2 Objetivos del Análisis

  1. Optimizar un portafolio de tres acciones mediante el modelo de Media-Varianza maximizando el ratio de Sharpe
  2. Simular trayectorias de precios utilizando el Movimiento Geométrico Browniano con esquema de Milstein
  3. Analizar métricas de riesgo (VaR, volatilidad, Sharpe) en múltiples horizontes temporales
  4. Valuar opciones europeas y americanas mediante árboles binomiales con dos escenarios de volatilidad
  5. Diseñar una estrategia de cobertura del 85% del capital con apalancamiento a tasa libre de riesgo
  6. Comparar la efectividad de diferentes estructuras de cobertura en tres escenarios de mercado

1.2 Parámetros del Proyecto

Tabla 1. Especificaciones Técnicas del Proyecto de Inversión y Cobertura
Parámetro Especificación Justificación Técnica
Capital Inicial de Inversión $10,000,000 USD Monto significativo para análisis institucional
Horizonte Temporal de Inversión 10 años (2,520 días hábiles) Horizonte largo plazo típico para fondos de pensión
Fecha Inicio de Datos Históricos 01 de octubre de 2023 Período con datos recientes y completos
Tasa Libre de Riesgo (Bono 10Y) 4.15% anual continua Tasa de mercado observada al 08/11/2025
Porcentaje de Cobertura 85% del capital total Balance entre protección y costo de cobertura
Fuente de Apalancamiento Tasa de Bono del Tesoro USA 10Y Costo de oportunidad del capital ajustado al riesgo
Activos Seleccionados TSM, COST, JNJ Alta liquidez y capitalización de mercado
Criterio de Selección Acciones sin dividendos relevantes Evita complejidad en valoración de opciones
Frecuencia de Liquidación Trimestral (cada 63 días hábiles) Liquidez óptima en mercado de opciones
Horizonte Máximo de Opciones 2 años (8 trimestres) Balance liquidez vs protección de largo plazo
Número de Simulaciones MGB 500 trayectorias independientes Suficiente para análisis distribucional robusto
Pasos en Árbol Binomial 8 pasos por opción Convergencia comprobada a modelo continuo

Nota metodológica: Todos los cálculos se realizan con capitalización continua para consistencia con modelos teóricos de valuación de derivados (Black & Scholes, 1973; Cox et al., 1979).


2 Marco Teórico y Metodología

2.1 Modelo de Media-Varianza de Markowitz

El modelo de optimización de portafolios de Markowitz (1952) busca la frontera eficiente de inversiones mediante la maximización del ratio de Sharpe:

\[ \max_{w} \frac{E[R_p] - r_f}{\sigma_p} = \frac{w^T \mu - r_f}{\sqrt{w^T \Sigma w}} \]

donde \(w\) es el vector de pesos del portafolio, \(\mu\) el vector de retornos esperados, \(\Sigma\) la matriz de covarianza, y \(r_f\) la tasa libre de riesgo (Sharpe, 1966).

2.1.1 Supuestos del Modelo

  • Los retornos siguen una distribución normal multivariada
  • Los inversionistas son aversos al riesgo y racionales
  • No existen costos de transacción ni restricciones de ventas en corto
  • Los parámetros son conocidos y constantes en el horizonte de decisión

2.2 Movimiento Geométrico Browniano con Esquema de Milstein

La evolución estocástica de los precios de activos se modela mediante:

\[ dS_t = \mu S_t dt + \sigma S_t dW_t \]

El esquema de Milstein (1974) proporciona una aproximación de orden superior al método de Euler-Maruyama:

\[ S_{t+\Delta t} = S_t + \mu S_t \Delta t + \sigma S_t \sqrt{\Delta t} Z + \frac{1}{2}\sigma^2 S_t [(\sqrt{\Delta t} Z)^2 - \Delta t] \]

donde \(Z \sim N(0,1)\) es una variable aleatoria normal estándar. Este esquema reduce el error de aproximación de \(O(\Delta t)\) a \(O(\Delta t^{3/2})\) (Kloeden & Platen, 1992).

2.3 Modelo Binomial de Cox-Ross-Rubinstein

Cox, Ross y Rubinstein (1979) desarrollaron el modelo de valuación binomial basado en arbitraje y valuación neutral al riesgo. Para \(n\) pasos temporales:

\[ u = e^{\sigma\sqrt{\Delta t}}, \quad d = e^{-\sigma\sqrt{\Delta t}}, \quad p = \frac{e^{r\Delta t} - d}{u - d} \]

El precio de la opción se calcula mediante inducción hacia atrás:

\[ f_0 = e^{-rT} \sum_{j=0}^{n} \binom{n}{j} p^{n-j}(1-p)^j \max(S_0 u^{n-j}d^j - K, 0) \]

para una opción call europea. Las opciones americanas requieren comparación en cada nodo entre ejercicio inmediato y continuación.

2.4 Volatilidad Implícita y Modelo GARCH

La volatilidad implícita \(\sigma_{impl}\) es aquella que, al ser insertada en el modelo de Black-Scholes o binomial, replica el precio de mercado observado. Como señalan Dumas, Fleming y Whaley (1998), la volatilidad implícita captura las expectativas del mercado sobre volatilidad futura.

El modelo GARCH(1,1) de Bollerslev (1986) modela la heterocedasticidad condicional:

\[ \sigma_t^2 = \omega + \alpha \epsilon_{t-1}^2 + \beta \sigma_{t-1}^2 \]

proporcionando estimaciones dinámicas de volatilidad que incorporan clustering de volatilidad observado en series financieras.


3 Precios y Retornos

## [1] "TSM"  "COST" "JNJ"

3.1 Resumen de Precios Históricos

# Tabla resumen de precios
precios_resumen <- data.frame(
  Activo = activos,
  "Precio Inicial ($)" = as.numeric(data_precios[1, ]),
  "Precio Final ($)" = as.numeric(tail(data_precios, 1)),
  "Precio Mínimo ($)" = apply(data_precios, 2, min),
  "Precio Máximo ($)" = apply(data_precios, 2, max),
  "Retorno Acumulado (%)" = (as.numeric(tail(data_precios, 1)) / 
                         as.numeric(data_precios[1, ]) - 1) * 100,
  check.names = FALSE
)

precios_resumen %>%
  gt() %>%
  tab_header(
    title = "Tabla 2. Resumen de Precios Históricos",
    subtitle = paste("Período:", format(start_date, "%d/%m/%Y"), "-", 
                    format(end_date, "%d/%m/%Y"))
  ) %>%
  fmt_currency(columns = 2:5, currency = "USD", decimals = 2) %>%
  fmt_number(columns = 6, decimals = 2) %>%
  opt_align_table_header(align = "center") %>%
  tab_source_note(
    source_note = "Fuente: Yahoo Finance. Cálculos propios."
  ) %>%
  data_color(
    columns = 6,
    colors = scales::col_numeric(
      palette = c("#E74C3C", "#F39C12", "#2ECC71"),
      domain = NULL
    )
  )
Tabla 2. Resumen de Precios Históricos
Período: 01/10/2023 - 07/11/2025
Activo Precio Inicial ($) Precio Final ($) Precio Mínimo ($) Precio Máximo ($) Retorno Acumulado (%)
TSM $87.65 $289.24 $85.41 $305.09 229.99
COST $571.31 $923.58 $543.03 $1,076.86 61.66
JNJ $155.15 $186.97 $142.06 $193.72 20.51
Fuente: Yahoo Finance. Cálculos propios.

Durante el período analizado, los resultados mostraron que TSM obtuvo el mayor crecimiento, con un retorno acumulado del 229,99%, seguido por COST con 61,66% y JNJ con 20,51%. Estos valores reflejan un comportamiento diferenciado entre sectores, donde el activo tecnológico (TSM) destacó por su fuerte apreciación, en contraste con el desempeño más moderado de los activos de consumo (COST) y farmacéutico (JNJ). El comportamiento observado coincide con la literatura sobre la sensibilidad sectorial frente al riesgo sistemático, en la cual los sectores tecnológicos suelen mostrar mayores rendimientos esperados debido a su mayor exposición al riesgo.

3.2 Estadísticas Descriptivas de Retornos

# Anualización con capitalización continua (método correcto)
mu_diario <- apply(df_retornos, 2, mean, na.rm = TRUE)
mu_trim <- exp(mu_diario * dias_trim_ano) - 1
mu_anual <- exp(mu_diario * dias_trading_ano) - 1

sigma_diario <- apply(df_retornos, 2, sd, na.rm = TRUE)
sigma_trim <- apply(df_retornos, 2, function(x) sqrt(var(na.omit(x)) * dias_trim_ano))
sigma_anual <- apply(df_retornos, 2, function(x) sqrt(var(na.omit(x)) * dias_trading_ano))

# Estadísticos adicionales
skewness_ret <- apply(df_retornos, 2, function(x) {
  mean((x - mean(x))^3) / sd(x)^3
})

kurtosis_ret <- apply(df_retornos, 2, function(x) {
  mean((x - mean(x))^4) / sd(x)^4 - 3
})

# Tabla completa
tabla_estadisticas <- tibble(
  Activo = activos,
  "Retorno Diario (%)" = mu_diario * 100,
  "Retorno Trimestral (%)" = mu_trim * 100,
  "Retorno Anual (%)" = mu_anual * 100,
  "Vol. Diaria (%)" = sigma_diario * 100,
  "Vol. Trimestral (%)" = sigma_trim * 100,
  "Vol. Anual (%)" = sigma_anual * 100,
  "Asimetría" = skewness_ret,
  "Curtosis" = kurtosis_ret,
  "Observaciones" = nrow(df_retornos)
)

tabla_estadisticas %>%
  gt() %>%
  tab_header(
    title = "Tabla 3. Estadísticas Descriptivas de Retornos Logarítmicos",
    subtitle = "Análisis univariado por activo"
  ) %>%
  fmt_number(columns = 2:7, decimals = 4) %>%
  fmt_number(columns = 8, decimals = 0) %>%
  opt_align_table_header(align = "center") %>%
  tab_spanner(
    label = "Retornos Esperados",
    columns = c(2, 3, 4)
  ) %>%
  tab_spanner(
    label = "Volatilidad",
    columns = c(5, 6, 7)
  ) %>%
  tab_spanner(
    label = "Momentos Superiores",
    columns = c(8, 9, 10)
  ) %>%
  tab_source_note(
    source_note = "Nota: Retornos anualizados con capitalización continua. Volatilidad por método de desviación estándar."
  )
Tabla 3. Estadísticas Descriptivas de Retornos Logarítmicos
Análisis univariado por activo
Activo
Retornos Esperados
Volatilidad
Momentos Superiores
Retorno Diario (%) Retorno Trimestral (%) Retorno Anual (%) Vol. Diaria (%) Vol. Trimestral (%) Vol. Anual (%) Asimetría Curtosis Observaciones
TSM 0.2265 15.3412 76.9859 2.5239 20.0332 40.0663 0 3.371 527
COST 0.0911 5.9101 25.8199 1.2991 10.3110 20.6221 −1 5.045 527
JNJ 0.0354 2.2552 9.3307 1.0888 8.6422 17.2844 0 7.489 527
Nota: Retornos anualizados con capitalización continua. Volatilidad por método de desviación estándar.

Se observa que TSM obtuvo el mayor retorno anual (76,98%) junto con la mayor volatilidad (40,07%), lo que muestra un comportamiento más arriesgado, pero también más rentable. Por su parte, COST presentó un rendimiento medio (25,82%) y JNJ fue el activo más estable, con el menor retorno (9,33%) y la menor volatilidad (17,28%). Estos resultados confirman la relación entre riesgo y rentabilidad descrita en la teoría de portafolios (Markowitz, 1952). En general, los datos muestran que TSM aporta el mayor potencial de crecimiento, mientras que JNJ ayuda a equilibrar el portafolio por su estabilidad.

3.3 Matriz de Correlación

# Calcular matriz de correlación
matriz_corr <- cor(df_retornos, use = "complete.obs")

# Crear tabla formateada
matriz_corr_df <- as.data.frame(matriz_corr)
matriz_corr_df <- cbind(Activo = rownames(matriz_corr_df), matriz_corr_df)
rownames(matriz_corr_df) <- NULL

kable(matriz_corr_df,
      format = "html",
      digits = 4,
      caption = "Tabla 4. Matriz de Correlación de Retornos Diarios",
      col.names = c("Activo", activos)) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  ) %>%
  column_spec(1, bold = TRUE) %>%
  footnote(
    general = "Una correlación positiva indica movimientos en la misma dirección. Valores cercanos a 1 indican alta correlación.",
    general_title = "Interpretación:"
  )
Tabla 4. Matriz de Correlación de Retornos Diarios
Activo TSM COST JNJ
TSM 1.0000 0.2558 -0.2070
COST 0.2558 1.0000 0.0765
JNJ -0.2070 0.0765 1.0000
Interpretación:
Una correlación positiva indica movimientos en la misma dirección. Valores cercanos a 1 indican alta correlación.

La matriz de correlación presentada indica que existe una correlación positiva moderada entre TSM y COST (0.2558), mientras que JNJ muestra una correlación negativa con TSM (-0.2070) y una correlación baja y positiva con COST (0.0765). Estos resultados evidencian que los activos pertenecen a sectores con comportamientos diferenciados, lo que favorece la diversificación y la reducción del riesgo global del portafolio. Según la teoría moderna de portafolio, aprovechar combinaciones de activos con correlaciones bajas o negativas incrementa la eficiencia y estabilidad de la cartera al mitigar el impacto de movimientos adversos en mercados específicos (Markowitz, 1952).

3.4 Matriz de Covarianza

# Calcular la matriz de covarianza 
cov_mat <- cov(df_retornos, use = "complete.obs") * dias_trim_ano

# Formatear la matriz para presentación
cov_mat_df <- as.data.frame(round(cov_mat, 6))
cov_mat_df <- cbind(Activo = rownames(cov_mat_df), cov_mat_df)
rownames(cov_mat_df) <- NULL


kable(cov_mat_df,
      format = "html",
      digits = 6,
      caption = "Tabla 5. Matriz de Covarianza de Retornos Trimestrales",
      col.names = c("Activo", activos)) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  ) %>%
  column_spec(1, bold = TRUE) %>%
  footnote(
    general = "La covarianza indica cómo varía el rendimiento conjunto de dos activos durante un periodo de tiempo. Valores altos y positivos reflejan mayor movimiento en la misma dirección. Valores altos y negativos reflejan mayor movimiento en la dirreción contraria",
    general_title = "Interpretación:"
  )
Tabla 5. Matriz de Covarianza de Retornos Trimestrales
Activo TSM COST JNJ
TSM 0.040133 0.005284 -0.003583
COST 0.005284 0.010632 0.000681
JNJ -0.003583 0.000681 0.007469
Interpretación:
La covarianza indica cómo varía el rendimiento conjunto de dos activos durante un periodo de tiempo. Valores altos y positivos reflejan mayor movimiento en la misma dirección. Valores altos y negativos reflejan mayor movimiento en la dirreción contraria

La matriz de covarianza trimestral muestra una relación positiva moderada entre TSM y COST (0.005284) y una covarianza negativa entre TSM y JNJ (-0.003583), mientras que COST y JNJ presentan valores muy bajos y positivos (0.000681). Esto indica que los retornos de TSM y COST tienden a moverse en la misma dirección durante los trimestres analizados, mientras que TSM y JNJ presentan una tendencia opuesta. Valores positivos sugieren asociación directa y negativos asociación inversa, lo cual es utilizado en la teoría de portafolio para balancear el riesgo del portafolio y aprovechar la diversificación intersectorial (Markowitz, 1952).


4 Optimización de Portafolio Media-Varianza

4.1 Modelo de Markowitz con Máximo Ratio de Sharpe

El ratio de Sharpe (Sharpe, 1966) mide el exceso de retorno por unidad de riesgo:

\[ SR = \frac{E[R_p] - r_f}{\sigma_p} \]

La optimización busca los pesos óptimos \(w^*\) que maximizan este ratio sujeto a las restricciones:

\[ \begin{aligned} \max_w \quad & \frac{w^T\mu - r_f}{\sqrt{w^T\Sigma w}} \\ \text{s.a.} \quad & \sum_{i=1}^n w_i = 1 \\ & w_i \geq 0.1, \quad \forall i \end{aligned} \]

La restricción \(w_i \geq 0.1\) asegura diversificación mínima del 10% por activo.

4.2 Métricas de Desempeño del Portafolio

# ============================================================================
# MÉTRICAS ADICIONALES DEL PORTAFOLIO
# ============================================================================

# Beta del portafolio (asumiendo mercado como promedio equiponderado)
retorno_mercado <- mean(mu_trim)
var_mercado <- mean(diag(cov_mat))
cov_portafolio_mercado <- sum(pesos_optimos * mu_trim * retorno_mercado) - 
                          retorno_portafolio * retorno_mercado

# Tracking error vs portafolio equiponderado
pesos_equiponderado <- rep(1/n_activos, n_activos)
retorno_equiponderado <- sum(pesos_equiponderado * mu_trim)
vol_equiponderado <- sqrt(t(pesos_equiponderado) %*% cov_mat %*% pesos_equiponderado)
tracking_error <- sqrt(sum((pesos_optimos - pesos_equiponderado)^2 * diag(cov_mat)))

# Relación de información
exceso_retorno <- retorno_portafolio - retorno_equiponderado
information_ratio <- exceso_retorno / tracking_error

# Tabla de métricas
metricas_pf <- tibble(
  Métrica = c(
    "Retorno Esperado Trimestral",
    "Volatilidad Trimestral (Riesgo Total)",
    "Ratio de Sharpe",
    "Tasa Libre de Riesgo",
    "Exceso de Retorno sobre Rf",
    "Tracking Error vs Equiponderado",
    "Ratio de Información"
  ),
  Valor = c(
    retorno_portafolio,
    as.numeric(volatilidad_portafolio),
    as.numeric(sharpe_ratio),
    tasa_libre_riesgo,
    retorno_portafolio - tasa_libre_riesgo,
    tracking_error,
    information_ratio
  ),
  Interpretación = c(
    "Retorno promedio esperado en el próximo trimestre",
    "Desviación estándar de retornos trimestrales",
    "Exceso de retorno por unidad de riesgo",
    "Rendimiento del Bono del Tesoro USA 10Y",
    "Prima de riesgo del portafolio",
    "Desviación respecto a benchmark equiponderado",
    "Exceso de retorno por unidad de tracking error"
  )
)

metricas_pf %>%
  gt() %>%
  tab_header(
    title = "Tabla 6. Métricas de Desempeño del Portafolio Óptimo",
    subtitle = "Análisis de riesgo-retorno y benchmarking"
  ) %>%
  fmt_percent(columns = Valor, decimals = 2, rows = c(1, 2, 4, 5)) %>%
  fmt_number(columns = Valor, decimals = 4, rows = c(3, 7)) %>%
  opt_align_table_header(align = "center") %>%
  cols_width(
    Métrica ~ px(300),
    Valor ~ px(150),
    Interpretación ~ px(400)
  ) %>%
  tab_style(
    style = cell_fill(color = "#FFF3E0"),
    locations = cells_body(rows = 3)
  ) %>%
  tab_source_note(
    source_note = "Nota: Todas las métricas calculadas con datos históricos y supuesto de normalidad de retornos."
  )
Tabla 6. Métricas de Desempeño del Portafolio Óptimo
Análisis de riesgo-retorno y benchmarking
Métrica Valor Interpretación
Retorno Esperado Trimestral 7.84% Retorno promedio esperado en el próximo trimestre
Volatilidad Trimestral (Riesgo Total) 8.37% Desviación estándar de retornos trimestrales
Ratio de Sharpe 0.8145 Exceso de retorno por unidad de riesgo
Tasa Libre de Riesgo 1.02% Rendimiento del Bono del Tesoro USA 10Y
Exceso de Retorno sobre Rf 6.82% Prima de riesgo del portafolio
Tracking Error vs Equiponderado 0.0002062 Desviación respecto a benchmark equiponderado
Ratio de Información 0.1924 Exceso de retorno por unidad de tracking error
Nota: Todas las métricas calculadas con datos históricos y supuesto de normalidad de retornos.

La tabla de métricas resume el desempeño del portafolio optimizado en términos de riesgo y retorno. El retorno esperado trimestral (7.84%) supera significativamente la tasa libre de riesgo, y, con una volatilidad de 8.37%, refleja una adecuada relación rendimiento-riesgo. El ratio de Sharpe, que alcanza 0.8145, indica que el portafolio ofrece 0.81 unidades de retorno en exceso por cada unidad de riesgo, situándose en un nivel considerado bueno dentro de los estándares internacionales de inversión. El tracking error muy bajo evidencia una baja desviación respecto a un benchmark equiponderado. Finalmente, el ratio de información positivo corrobora la habilidad del portafolio para generar retornos superiores ajustados por riesgo, lo que valida la capacidad de la metodología de media-varianza para construir carteras eficientes en el contexto temporal analizado (Hull, 2018; Sharpe, 1994).

4.3 Asignación Óptima de Capital

# ============================================================================
# ASIGNACIÓN DE CAPITAL Y NÚMERO DE ACCIONES
# ============================================================================

# Montos asignados por activo
asignacion_capital <- pesos_optimos * inversion_inicial

# Número de acciones a comprar
num_acciones <- asignacion_capital / precios_actuales

# Tabla de asignación
tabla_portafolio <- tibble(
  Activo = nombres_activos,
  Ticker = activos,
  "Peso Óptimo (%)" = pesos_optimos * 100,
  "Capital Asignado ($)" = asignacion_capital,
  "Precio Actual ($)" = precios_actuales,
  "Número de Acciones" = num_acciones,
  "Valor de Mercado ($)" = num_acciones * precios_actuales
)

tabla_portafolio %>%
  gt() %>%
  tab_header(
    title = md("**Tabla 7. Portafolio Óptimo de Inversión**"),
    subtitle = paste("Inversión Total:", scales::dollar(inversion_inicial), 
                    "| Ratio de Sharpe:", round(sharpe_ratio, 4))
  ) %>%
  fmt_number(columns = 3, decimals = 2) %>%
  # Formatear columnas monetarias con código de moneda válido
  fmt_currency(columns = c(4, 5, 7), currency = "USD", decimals = 2) %>%
  fmt_number(columns = 6, decimals = 2) %>%
  opt_align_table_header(align = "center") %>%
  cols_align(align = "center", columns = everything()) %>%
  tab_style(
    style = cell_fill(color = "#E8F5E9"),
    locations = cells_body(columns = 3)
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_body(columns = c(1, 2))
  ) %>%
  # CORRECCIÓN: Usar fmt en lugar de formatter + currency
  grand_summary_rows(
    columns = c(4, 7),
    fns = list(Total = ~sum(.)),
    fmt = ~ fmt_currency(., currency = "USD", decimals = 2)
  ) %>%
  tab_source_note(
    source_note = md("*Fuente:* Optimización numérica con algoritmo L-BFGS-B. Restricción: peso mínimo 10% por activo.")
  ) 
Tabla 7. Portafolio Óptimo de Inversión
Inversión Total: $10,000,000 | Ratio de Sharpe: 0.8145
Activo Ticker Peso Óptimo (%) Capital Asignado ($) Precio Actual ($) Número de Acciones Valor de Mercado ($)
Taiwan Semiconductor TSM 33.40 $3,340,202.12 $289.24 11,548.20 $3,340,202.12
Costco Wholesale Corp. COST 33.20 $3,319,595.77 $923.58 3,594.27 $3,319,595.77
Johnson & Johnson JNJ 33.40 $3,340,202.12 $186.97 17,864.91 $3,340,202.12
Total $10,000,000.00 $10,000,000.00
Fuente: Optimización numérica con algoritmo L-BFGS-B. Restricción: peso mínimo 10% por activo.

La asignación de capital se distribuyó de manera equitativa entre los tres activos seleccionados, obteniendo pesos óptimos cercanos al 33% y respetando la restricción mínima de diversificación del 10% por activo. Este equilibrio permite maximizar el ratio de Sharpe del portafolio y reduce el riesgo específico de cada empresa, lo cual es consistente con los principios de la optimización de portafolios de Markowitz y la teoría de media-varianza. Además, el número de acciones y el valor de mercado aseguran una exposición adecuada en cada activo, contribuyendo a la estabilidad y robustez del portafolio ante fluctuaciones sectoriales y de mercado (Hull, 2018; Markowitz, 1952).


5 Análisis de Volatilidad: Histórica, Implícita y GARCH

5.1 Estimación de Volatilidad Histórica

La volatilidad histórica se estima mediante la desviación estándar muestral de retornos logarítmicos:

\[ \sigma_{hist} = s_{diaria} \times \sqrt{63} \]

donde \(s_{diaria}\) es la desviación estándar de retornos diarios.

5.2 Volatilidad Implícita de Mercado

La volatilidad implícita es aquella que, al ser utilizada en el modelo de Black-Scholes o en modelos binomiales, reproduce el precio de mercado observado de la opción. Este parámetro representa la expectativa agregada del mercado sobre la volatilidad futura del activo subyacente durante la vida de la opción (Hull2018?).

En este análisis, se emplean volatilidades implícitas de mercado trimestrales extraídas directamente de los datos de la cadena de opciones disponibles en Yahoo Finance para vencimiento enero 2028 y los strikes seleccionados para cada escenario (OTM, ATM y ITM). Las volatilidades difieren según la posición del strike respecto al spot, reflejando el típico comportamiento skew/smile observado en mercados financieros (Dumas et al., 1998). El procedimiento asegura que las estimaciones de precios y riesgos en el portafolio de derivados están alineadas con las expectativas reales del mercado (Finance, 2025).

5.3 Modelo GARCH(1,1) para Volatilidad Condicional

El modelo GARCH (Generalized Autoregressive Conditional Heteroskedasticity) de Bollerslev (1986) captura la persistencia y clustering de volatilidad:

\[ \begin{aligned} r_t &= \mu + \epsilon_t \\ \epsilon_t &= \sigma_t z_t, \quad z_t \sim N(0,1) \\ \sigma_t^2 &= \omega + \alpha \epsilon_{t-1}^2 + \beta \sigma_{t-1}^2 \end{aligned} \]

El modelo se estima por máxima verosimilitud y proporciona pronósticos dinámicos de volatilidad.

# ============================================================================
# ESTIMACIÓN DE VOLATILIDADES MEDIANTE MODELO GARCH(1,1)
# ============================================================================

calcular_vol_garch <- function(retornos, nombre_activo) {
  cat(sprintf("\n[INFO] Estimando GARCH(1,1) para %s...\n", nombre_activo))

  # PASO CRÍTICO: Limpiar valores NA, NaN e Inf
  retornos_limpios <- na.omit(retornos)
  
  # Verificar que haya suficientes datos después de la limpieza
  if (length(retornos_limpios) < 100) {
    stop(sprintf("  [ERROR] Datos insuficientes para %s después de eliminar NAs (n=%d)", 
                 nombre_activo, length(retornos_limpios)))
  }
  
  # Verificar que haya variabilidad en los datos
  if (sd(retornos_limpios, na.rm = TRUE) == 0) {
    stop(sprintf("  [ERROR] Los retornos de %s no tienen varianza", nombre_activo))
  }
  
  cat(sprintf("  → Datos válidos: %d observaciones\n", length(retornos_limpios)))
  cat(sprintf("  → Media: %.6f, Desv.Est: %.6f\n", 
              mean(retornos_limpios), sd(retornos_limpios)))

  # Especificación del modelo GARCH(1,1)
  spec <- ugarchspec(
    variance.model = list(model = "sGARCH", garchOrder = c(1, 1)),
    mean.model = list(armaOrder = c(0, 0), include.mean = TRUE),
    distribution.model = "norm"
  )

  # Estimación del modelo con manejo de errores robusto
  fit <- tryCatch({
    ugarchfit(spec, retornos_limpios, solver = "hybrid")
  }, error = function(e) {
    cat("  [ADVERTENCIA] Solver 'hybrid' falló. Intentando 'solnp'...\n")
    tryCatch({
      ugarchfit(spec, retornos_limpios, solver = "solnp")
    }, error = function(e2) {
      cat("  [ADVERTENCIA] Solver 'solnp' falló. Intentando 'nlminb'...\n")
      ugarchfit(spec, retornos_limpios, solver = "nlminb")
    })
  })

  # Verificar convergencia
  if (convergence(fit) != 0) {
    warning(sprintf("  [ADVERTENCIA] Modelo GARCH para %s no convergió completamente", 
                    nombre_activo))
  }

  # Extraer volatilidad condicional
  vol_condicional <- sigma(fit)
  vol_trim <- mean(vol_condicional) * sqrt(dias_trim_ano)
  vol_anual <- mean(vol_condicional) * sqrt(dias_trading_ano)

  # Parámetros estimados
  coef_garch <- coef(fit)

  cat(sprintf("  ✓ Volatilidad anual GARCH: %.2f%%\n", vol_anual * 100))
  cat(sprintf("  ✓ Volatilidad trimestral GARCH: %.2f%%\n", vol_trim * 100))
  cat(sprintf("    ω = %.6f, α = %.6f, β = %.6f\n", 
              coef_garch["omega"], coef_garch["alpha1"], coef_garch["beta1"]))
  cat(sprintf("    Persistencia (α+β): %.4f\n", 
              coef_garch["alpha1"] + coef_garch["beta1"]))

  return(list(
    volatilidad_trim = vol_trim,
    volatilidad_anual = vol_anual,
    serie_vol = vol_condicional,
    modelo = fit,
    parametros = coef_garch,
    n_obs = length(retornos_limpios)
  ))
}


# Estimar GARCH para cada activo
vol_garch_TSM <- calcular_vol_garch(df_retornos$TSM, "TSM")
## 
## [INFO] Estimando GARCH(1,1) para TSM...
##   → Datos válidos: 527 observaciones
##   → Media: 0.002265, Desv.Est: 0.025239
##   ✓ Volatilidad anual GARCH: 40.08%
##   ✓ Volatilidad trimestral GARCH: 20.04%
##     ω = 0.000033, α = 0.029454, β = 0.919960
##     Persistencia (α+β): 0.9494
vol_garch_COST <- calcular_vol_garch(df_retornos$COST, "COST")
## 
## [INFO] Estimando GARCH(1,1) para COST...
##   → Datos válidos: 527 observaciones
##   → Media: 0.000911, Desv.Est: 0.012991
##   ✓ Volatilidad anual GARCH: 20.99%
##   ✓ Volatilidad trimestral GARCH: 10.50%
##     ω = 0.000000, α = 0.000080, β = 0.997537
##     Persistencia (α+β): 0.9976
vol_garch_JNJ <- calcular_vol_garch(df_retornos$JNJ, "JNJ")
## 
## [INFO] Estimando GARCH(1,1) para JNJ...
##   → Datos válidos: 527 observaciones
##   → Media: 0.000354, Desv.Est: 0.010888
##   ✓ Volatilidad anual GARCH: 17.25%
##   ✓ Volatilidad trimestral GARCH: 8.63%
##     ω = 0.000083, α = 0.362404, β = 0.016796
##     Persistencia (α+β): 0.3792
# Volatilidades implícitas aproximadas del mercado
vol_impl <- c(
  TSM = 0.1820,   # 18.20% 
  COST = 0.1030,   # 10.30% 
  JNJ = 0.1083    # 10.83% 
)


# Volatilidades implícitas según escenario/strike

vol_impl_por_escenario <- list(
  BAJO  = c(TSM = 0.1832, COST = 0.1081, JNJ = 0.1082),  # OTM
  MEDIO = c(TSM = 0.1795, COST = 0.1042, JNJ = 0.1013),  # ATM  
  ALTO  = c(TSM = 0.1747, COST = 0.0971, JNJ = 0.1006)   # ITM
)

5.4 Comparación de Métodos de Volatilidad

# ============================================================================
# TABLA COMPARATIVA DE VOLATILIDADES
# ============================================================================

# Compilar resultados
vol_garch_valores <- c(
  vol_garch_TSM$volatilidad_trim,
  vol_garch_COST$volatilidad_trim,
  vol_garch_JNJ$volatilidad_trim
)

# Tabla comparativa
tabla_vol <- tibble(
  Escenario = rep(c("BAJO", "MEDIO", "ALTO"), each = length(activos)),
  Activo = rep(activos, 3),
  Nombre = rep(nombres_activos, 3),
  "Vol. Histórica (%)" = rep(sigma_trim, 3) * 100,
  "Vol. Implícita (%)" = c(vol_impl_por_escenario$BAJO, 
                               vol_impl_por_escenario$MEDIO, 
                               vol_impl_por_escenario$ALTO) * 100,
  "Vol. GARCH (%)" = rep(vol_garch_valores, 3) * 100,
  "Spread Impl-Hist (%)" = c(vol_impl_por_escenario$BAJO, 
                               vol_impl_por_escenario$MEDIO, 
                               vol_impl_por_escenario$ALTO) * 100 - rep(sigma_trim, 3) * 100,
  "Spread GARCH-Hist (%)" = rep(vol_garch_valores, 3) * 100 - rep(sigma_trim, 3) * 100
)

tabla_vol %>%
  gt() %>%
  tab_header(
    title = md("**Tabla 8. Comparación de Metodologías de Estimación de Volatilidad**"),
    subtitle = "Volatilidades trimestrales expresadas en porcentaje"
  ) %>%
  fmt_number(columns = 5:8, decimals = 2) %>%
  opt_align_table_header(align = "center") %>%
  tab_spanner(
    label = "Volatilidades Trimestrales",
    columns = 4:6
  ) %>%
  tab_spanner(
    label = "Diferencias (Spreads)",
    columns = 7:8
  ) %>%
  data_color(
    columns = 5:7,
    colors = scales::col_numeric(
      palette = c("#E8F5E9", "#FFF9C4", "#FFCDD2"),
      domain = range(tabla_vol$'Vol. Histórica (%)',
                     tabla_vol$'Vol. Implícita (%)',
                     tabla_vol$'Vol. GARCH (%)')
    )
  ) %>%
  data_color(
    columns = 8,
    colors = scales::col_numeric(
      palette = c("#BBDEFB", "white", "#FFCCBC"),
      domain = range(tabla_vol$'Spread Impl-Hist (%)',
                     tabla_vol$'Spread GARCH-Hist (%)')
    )
  ) %>%
  tab_source_note(
    source_note = md("*Fuentes:* Vol. Histórica: cálculo propio. Vol. Implícita: aproximación de mercado ATM 3M. Vol. GARCH: modelo GARCH(1,1) estimado.")
  ) %>%
  tab_footnote(
    footnote = "Un spread positivo indica que la vol. implícita/GARCH es mayor que la histórica, sugiriendo expectativas de mayor volatilidad futura.",
    locations = cells_column_labels(columns = 8)
  )
Tabla 8. Comparación de Metodologías de Estimación de Volatilidad
Volatilidades trimestrales expresadas en porcentaje
Escenario Activo Nombre
Volatilidades Trimestrales
Diferencias (Spreads)
Vol. Histórica (%) Vol. Implícita (%) Vol. GARCH (%) Spread Impl-Hist (%) Spread GARCH-Hist (%)1
BAJO TSM Taiwan Semiconductor 20.033 18.32 20.04 −1.71 0.01
BAJO COST Costco Wholesale Corp. 10.311 10.81 10.50 0.50 0.19
BAJO JNJ Johnson & Johnson 8.642 10.82 8.63 2.18 −0.02
MEDIO TSM Taiwan Semiconductor 20.033 17.95 20.04 −2.08 0.01
MEDIO COST Costco Wholesale Corp. 10.311 10.42 10.50 0.11 0.19
MEDIO JNJ Johnson & Johnson 8.642 10.13 8.63 1.49 −0.02
ALTO TSM Taiwan Semiconductor 20.033 17.47 20.04 −2.56 0.01
ALTO COST Costco Wholesale Corp. 10.311 9.71 10.50 −0.60 0.19
ALTO JNJ Johnson & Johnson 8.642 10.06 8.63 1.42 −0.02
1 Un spread positivo indica que la vol. implícita/GARCH es mayor que la histórica, sugiriendo expectativas de mayor volatilidad futura.
Fuentes: Vol. Histórica: cálculo propio. Vol. Implícita: aproximación de mercado ATM 3M. Vol. GARCH: modelo GARCH(1,1) estimado.

5.4.1 Gráfico de Volatilidad Condicional GARCH

# ============================================================================
# VISUALIZACIÓN DE VOLATILIDAD CONDICIONAL
# ============================================================================

# Preparar datos para gráficos
df_vol_TSM <- data.frame(
  Fecha = index(retornos),
  Vol = as.numeric(vol_garch_TSM$serie_vol) * sqrt(63) * 100,
  Activo = "TSM"
)

df_vol_COST <- data.frame(
  Fecha = index(retornos),
  Vol = as.numeric(vol_garch_COST$serie_vol) * sqrt(63) * 100,
  Activo = "COST"
)

df_vol_JNJ <- data.frame(
  Fecha = index(retornos),
  Vol = as.numeric(vol_garch_JNJ$serie_vol) * sqrt(63) * 100,
  Activo = "JNJ"
)

df_vol_completo <- rbind(df_vol_TSM, df_vol_COST, df_vol_JNJ)

# Gráfico de volatilidad condicional
ggplot(df_vol_completo, aes(x = Fecha, y = Vol, color = Activo)) +
  geom_line(linewidth = 0.8) +
  facet_wrap(~Activo, ncol = 1, scales = "free_y") +
  scale_color_manual(values = c("TSM" = "#E74C3C", 
                                "COST" = "#3498DB", 
                                "JNJ" = "#2ECC71")) +
  scale_y_continuous(labels = function(x) paste0(x, "%")) +
  labs(
    title = "Figura 1. Volatilidad Condicional Estimada con Modelo GARCH(1,1)",
    subtitle = "Volatilidad trimestral en porcentaje",
    x = "Fecha",
    y = "Volatilidad Trimestral (%)",
    caption = "Fuente: Elaboración propia con datos de Yahoo Finance. Modelo GARCH(1,1) estimado por máxima verosimilitud."
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    strip.text = element_text(face = "bold", size = 12),
    legend.position = "none",
    panel.grid.minor = element_blank()
  )

Análisis de clustering de volatilidad: Los gráficos evidencian el fenómeno de “volatility clustering” (Mandelbrot, 1963), donde períodos de alta volatilidad tienden a seguir a períodos de alta volatilidad, y viceversa. El modelo GARCH captura exitosamente este patrón mediante los parámetros \(\alpha\) y \(\beta\), donde \(\alpha + \beta\) cercano a 1 indica alta persistencia de shocks de volatilidad.


6 Simulación de Precios con Movimiento Geométrico Browniano

6.1 Esquema de Milstein para Discretización Estocástica

El esquema de Milstein (1974) proporciona una aproximación de orden superior al método de Euler-Maruyama para ecuaciones diferenciales estocásticas. Para la EDE del precio de un activo:

\[ dS_t = \mu S_t dt + \sigma S_t dW_t \]

el esquema de Milstein es:

\[ S_{t+\Delta t} = S_t \left[1 + \mu \Delta t + \sigma \sqrt{\Delta t} Z_t + \frac{1}{2}\sigma^2 (\Delta t Z_t^2 - \Delta t)\right] \]

donde \(Z_t \sim N(0,1)\) son variables aleatorias independientes. El término \(\frac{1}{2}\sigma^2 (\Delta t Z_t^2 - \Delta t)\) es la corrección de Milstein que reduce el error de \(O(\Delta t)\) a \(O(\Delta t^{3/2})\).

6.2 Simulaciones para Cada Activo

## Taiwan Semiconductor  μ (continua) = 0.1534 (15.34% trimestral)
##   σ (trimestral)    = 0.2003 (20.03%)
##   S0           = $289.24
## Costco Wholesale Corp.  μ (continua) = 0.0591 (5.91% trimestral)
##   σ (trimestral)    = 0.1031 (10.31%)
##   S0           = $923.58
## Johnson & Johnson  μ (continua) = 0.0226 (2.26% trimestral)
##   σ (trimestral)    = 0.0864 (8.64%)
##   S0           = $186.97

6.3 Selección de Trayectorias Extremas (Alto, Medio, Bajo)

Para el análisis de cobertura, se seleccionan tres escenarios representativos:

  • Escenario Bajo: Trayectoria con el valor final más bajo (percentil 0)
  • Escenario Medio: Trayectoria cercana a la mediana (percentil 50)
  • Escenario Alto: Trayectoria con el valor final más alto (percentil 100)

6.3.1 Tabla de Precios por Escenario y Activo

# Crear tabla con precios finales por escenario
tabla_precios_escenarios <- tibble(
  Activo = rep(activos, 3),
  Escenario = rep(c("Bajo", "Medio", "Alto"), each = 3),
  "Precio Inicial ($)" = rep(precios_actuales, 3),
  "Precio Final ($)" = c(
    valores_finales$TSM[idx_baja], 
    valores_finales$COST[idx_baja], 
    valores_finales$JNJ[idx_baja],
    valores_finales$TSM[idx_media], 
    valores_finales$COST[idx_media], 
    valores_finales$JNJ[idx_media],
    valores_finales$TSM[idx_alta], 
    valores_finales$COST[idx_alta], 
    valores_finales$JNJ[idx_alta]
  ),
  "Retorno Acumulado (%)" = (c(
    valores_finales$TSM[idx_baja], 
    valores_finales$COST[idx_baja], 
    valores_finales$JNJ[idx_baja],
    valores_finales$TSM[idx_media], 
    valores_finales$COST[idx_media], 
    valores_finales$JNJ[idx_media],
    valores_finales$TSM[idx_alta], 
    valores_finales$COST[idx_alta], 
    valores_finales$JNJ[idx_alta]
  ) / rep(precios_actuales, 3) - 1) * 100,
  "Valor Posición ($)" = c(
    num_acciones[1] * valores_finales$TSM[idx_baja],
    num_acciones[2] * valores_finales$COST[idx_baja],
    num_acciones[3] * valores_finales$JNJ[idx_baja],
    num_acciones[1] * valores_finales$TSM[idx_media],
    num_acciones[2] * valores_finales$COST[idx_media],
    num_acciones[3] * valores_finales$JNJ[idx_media],
    num_acciones[1] * valores_finales$TSM[idx_alta],
    num_acciones[2] * valores_finales$COST[idx_alta],
    num_acciones[3] * valores_finales$JNJ[idx_alta]
  )
)

tabla_precios_escenarios %>%
  gt() %>%
  tab_header(
    title = "Tabla 9. Precios Simulados por Escenario al Año 10",
    subtitle = "Tres trayectorias representativas: Bajo (mínimo), Medio (mediana), Alto (máximo)"
  ) %>%
  fmt_currency(columns = c(3, 4, 6), currency = "USD", decimals = 2) %>%
  fmt_number(columns = 5, decimals = 2) %>%
  opt_align_table_header(align = "center") %>%
  data_color(
    columns = 5,
    colors = scales::col_numeric(
      palette = c("#E74C3C", "#F39C12", "#2ECC71"),
      domain = NULL
    )
  ) %>%
  tab_row_group(
    label = "Escenario Alto",
    rows = Escenario == "Alto"
  ) %>%
  tab_row_group(
    label = "Escenario Medio",
    rows = Escenario == "Medio"
  ) %>%
  tab_row_group(
    label = "Escenario Bajo",
    rows = Escenario == "Bajo"
  ) %>%
  summary_rows(
    groups = TRUE,
    columns = 6,
    fns = list(Total = ~sum(.)),
    formatter = fmt_currency,
    currency = "USD"
  ) %>%
  tab_source_note(
    source_note = "Fuente: Simulación con MGB y esquema de Milstein. Horizonte: 10 años."
  )
Tabla 9. Precios Simulados por Escenario al Año 10
Tres trayectorias representativas: Bajo (mínimo), Medio (mediana), Alto (máximo)
Activo Escenario Precio Inicial ($) Precio Final ($) Retorno Acumulado (%) Valor Posición ($)
Escenario Bajo
TSM Bajo $289.24 $308.98 6.83 $3,568,210.46
COST Bajo $923.58 $864.19 −6.43 $3,106,117.74
JNJ Bajo $186.97 $171.39 −8.33 $3,061,914.26
Escenario Medio
TSM Medio $289.24 $344.89 19.24 $3,982,829.87
COST Medio $923.58 $882.81 −4.41 $3,173,058.65
JNJ Medio $186.97 $186.20 −0.41 $3,326,458.54
Escenario Alto
TSM Alto $289.24 $386.84 33.74 $4,467,328.65
COST Alto $923.58 $957.92 3.72 $3,443,014.27
JNJ Alto $186.97 $191.73 2.55 $3,425,242.19
Fuente: Simulación con MGB y esquema de Milstein. Horizonte: 10 años.

6.4 Visualización de Portafolios Simulados

# ============================================================================
# GRÁFICOS DE EVOLUCIÓN DE PORTAFOLIOS
# ============================================================================

# Preparar datos para gráfico
df_portafolios <- data.frame(
  Tiempo = sim_TSM$tiempo,
  Bajo = portafolio_bajo / 1e6,
  Medio = portafolio_medio / 1e6,
  Alto = portafolio_alto / 1e6
) %>%
  pivot_longer(cols = c(Bajo, Medio, Alto),
               names_to = "Escenario",
               values_to = "Valor")

# Factor para orden correcto
df_portafolios$Escenario <- factor(df_portafolios$Escenario, 
                                   levels = c("Bajo", "Medio", "Alto"))

# Gráfico principal
p1 <- ggplot(df_portafolios, aes(x = Tiempo, y = Valor, color = Escenario)) +
  geom_line(linewidth = 1.5, alpha = 0.8) +
  geom_hline(yintercept = inversion_inicial/1e6, 
             linetype = "dashed", color = "gray30", linewidth = 1) +
  scale_color_manual(values = c("Bajo" = "#E74C3C", 
                                "Medio" = "#3498DB", 
                                "Alto" = "#2ECC71")) +
  scale_y_continuous(labels = scales::dollar_format(suffix = "M", prefix = "$")) +
  labs(
    title = "Figura 2. Evolucion de Portafolios Simulados (Tres Escenarios)",
    subtitle = "Simulacion MGB con esquema de Milstein | Horizonte: 10 anos",
    x = "Anos desde inversion inicial",
    y = "Valor del Portafolio (Millones USD)",
    color = "Escenario"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "top",
    legend.title = element_text(face = "bold"),
    panel.grid.minor = element_blank()
  ) +
  annotate("text", x = 0.5, y = inversion_inicial/1e6 + 0.5, 
           label = "Capital Inicial", hjust = 0, size = 3.5, color = "gray30")

# Gráfico de distribución final
df_dist_final <- data.frame(
  Escenario = c("Bajo", "Medio", "Alto"),
  Valor = c(portafolio_bajo[length(portafolio_bajo)],
            portafolio_medio[length(portafolio_medio)],
            portafolio_alto[length(portafolio_alto)]) / 1e6
)

df_dist_final$Escenario <- factor(df_dist_final$Escenario, 
                                  levels = c("Bajo", "Medio", "Alto"))

p2 <- ggplot(df_dist_final, aes(x = Escenario, y = Valor, fill = Escenario)) +
  geom_col(width = 0.6, alpha = 0.8) +
  geom_hline(yintercept = inversion_inicial/1e6, linetype = "dashed", color = "gray30") +
  scale_fill_manual(values = c("Bajo" = "#E74C3C", 
                               "Medio" = "#3498DB", 
                               "Alto" = "#2ECC71")) +
  scale_y_continuous(labels = scales::dollar_format(suffix = "M", prefix = "$")) +
  geom_text(aes(label = scales::dollar(Valor, suffix = "M", accuracy = 0.1)),
            vjust = -0.5, fontface = "bold", size = 4) +
  labs(
    title = "Figura 3. Valor Final del Portafolio por Escenario",
    subtitle = "Comparacion al final del horizonte (Ano 10)",
    x = NULL,
    y = "Valor del Portafolio (Millones USD)"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5),
    plot.subtitle = element_text(size = 10, hjust = 0.5),
    legend.position = "none",
    panel.grid.major.x = element_blank()
  )

# Combinar gráficos
grid.arrange(p1, p2, ncol = 1, heights = c(3, 3))

Análisis de escenarios: La dispersión entre los tres escenarios refleja la incertidumbre inherente en la evolución de precios de activos riesgosos. El escenario medio representa la trayectoria más probable dada la distribución lognormal asumida, mientras que los escenarios extremos ilustran posibles desviaciones significativas que justifican estrategias de cobertura.


7 Análisis de Riesgo (VaR)

7.1 Fundamento Teórico del Value at Risk

El Value at Risk (VaR) es la pérdida máxima esperada en un portafolio durante un horizonte temporal específico con un nivel de confianza determinado. Formalmente, para un nivel de confianza \(\alpha\):

\[ P(\Delta V \leq -\text{VaR}_{\alpha}) = 1 - \alpha \]

donde \(\Delta V\) es el cambio en el valor del portafolio. El VaR es ampliamente utilizado por instituciones financieras y reguladores como medida estándar de riesgo de mercado (Jorion, 2007).

7.1.1 Metodología de Cálculo

Empleamos el método histórico con simulación Monte Carlo, que:

  1. Genera \(N\) trayectorias de precios mediante MGB con esquema de Milstein
  2. Calcula el valor del portafolio en cada trayectoria
  3. Estima el VaR como el percentil \((1-\alpha)\) de la distribución de pérdidas

Esta aproximación captura la no-linealidad y distribución real de retornos sin asumir normalidad.

7.2 Tabla Consolidada de VaR

# ============================================================================
# TABLA MAESTRA DE VaR
# ============================================================================

# Convertir resultados a data frame
df_var <- do.call(rbind, lapply(resultados_var, function(x) {
  data.frame(
    Horizonte = x$horizonte,
    "Trimestres" = x$dias,
    "Nivel Confianza" = paste0(x$nivel_confianza * 100, "%"),
    "VaR ($)" = x$var,
    "Pérdida Máxima ($)" = x$perdida,
    "Pérdida (%)" = abs(x$perdida_pct),
    "Valor Esperado ($)" = x$valor_esperado,
    "Desv. Estándar ($)" = x$desviacion_std,
    check.names = FALSE,
    stringsAsFactors = FALSE
  )
}))

# Tabla interactiva con DT
DT::datatable(
  df_var,
  rownames = FALSE,
  caption = "Tabla 10. Value at Risk (VaR) por Horizonte Temporal y Nivel de Confianza",
  options = list(
    pageLength = 15,
    autoWidth = TRUE,
    order = list(list(2, 'asc'), list(3, 'desc')),
    dom = 'Bfrtip',
    buttons = c('copy', 'csv', 'excel', 'pdf')
  ),
  extensions = 'Buttons'
) %>%
  formatCurrency(c("VaR ($)", "Pérdida Máxima ($)", "Valor Esperado ($)", "Desv. Estándar ($)"),
                 currency = "$", digits = 0) %>%
  formatPercentage("Pérdida (%)", digits = 2) %>%
  formatStyle(
    "Pérdida (%)",
    background = styleColorBar(range(df_var$`Pérdida (%)`), '#FFA07A'),
    backgroundSize = '100% 90%',
    backgroundRepeat = 'no-repeat',
    backgroundPosition = 'center'
  )

7.2.1 Análisis Detallado por Horizonte

# Enfocarse en horizontes clave
horizontes_clave <- c("6 meses", "1 año", "2 años", "10 años")

df_var_clave <- df_var %>%
  filter(Horizonte %in% horizontes_clave) %>%
  arrange(Trimestres, desc(`Nivel Confianza`))

df_var_clave %>%
  gt() %>%
  tab_header(
    title = "Tabla 11. VaR en Horizontes Clave de Decisión",
    subtitle = "Análisis de riesgo para horizontes estratégicos"
  ) %>%
  fmt_currency(columns = c("VaR ($)", "Pérdida Máxima ($)", 
                           "Valor Esperado ($)", "Desv. Estándar ($)"),
               currency = "USD", decimals = 0) %>%
  fmt_number(columns = "Pérdida (%)", decimals = 2) %>%
  opt_align_table_header(align = "center") %>%
  tab_row_group(
    label = "Largo Plazo (10 años)",
    rows = Horizonte == "10 años"
  ) %>%
  tab_row_group(
    label = "Mediano Plazo (2 años)",
    rows = Horizonte == "2 años"
  ) %>%
  tab_row_group(
    label = "Corto Plazo (1 año)",
    rows = Horizonte == "1 año"
  ) %>%
  tab_row_group(
    label = "Muy Corto Plazo (6 meses)",
    rows = Horizonte == "6 meses"
  ) %>%
  data_color(
    columns = "Pérdida (%)",
    colors = scales::col_numeric(
      palette = c("#E8F5E9", "#FFF9C4", "#FFCDD2", "#EF5350"),
      domain = NULL
    )
  ) %>%
  tab_source_note(
    source_note = md("*Metodología:* VaR calculado mediante simulación Monte Carlo con 500 trayectorias. Modelo: MGB con esquema de Milstein.")
  )
Tabla 11. VaR en Horizontes Clave de Decisión
Análisis de riesgo para horizontes estratégicos
Horizonte Trimestres Nivel Confianza VaR ($) Pérdida Máxima ($) Pérdida (%) Valor Esperado ($) Desv. Estándar ($)
Muy Corto Plazo (6 meses)
6 meses 2 99% $9,762,237 $237,763 0.98 $10,004,243 $105,418
6 meses 2 95% $9,825,096 $174,904 0.98 $10,004,243 $105,418
Corto Plazo (1 año)
1 año 4 99% $9,662,272 $337,728 0.97 $10,031,364 $170,171
1 año 4 95% $9,753,106 $246,894 0.98 $10,031,364 $170,171
Mediano Plazo (2 años)
2 años 8 99% $9,481,204 $518,796 0.95 $10,075,733 $269,079
2 años 8 95% $9,652,290 $347,710 0.97 $10,075,733 $269,079
Largo Plazo (10 años)
10 años 40 99% $9,090,950 $909,050 0.91 $10,484,683 $653,934
10 años 40 95% $9,526,510 $473,490 0.95 $10,484,683 $653,934
Metodología: VaR calculado mediante simulación Monte Carlo con 500 trayectorias. Modelo: MGB con esquema de Milstein.

7.3 Visualización de Distribuciones de VaR

# ============================================================================
# GRÁFICOS DE DISTRIBUCIÓN Y VaR
# ============================================================================

library(ggplot2)
library(gridExtra)

# Preparar datos para histogramas
crear_histograma_var <- function(horizonte_nombre, horizonte_dias_val) {

  if(horizonte_dias_val > nrow(valor_portafolio_sim)) {
    return(NULL)
  }

  valores <- valor_portafolio_sim[horizonte_dias_val, ] / 1e6

  var_99 <- quantile(valores, 0.01)
  var_95 <- quantile(valores, 0.05)
  media <- mean(valores)

  df_hist <- data.frame(Valor = valores)

  p <- ggplot(df_hist, aes(x = Valor)) +
    geom_histogram(aes(y = after_stat(density)), 
                   bins = 40, 
                   fill = "#3498DB", 
                   color = "white", 
                   alpha = 0.7) +
    geom_density(color = "#E74C3C", linewidth = 1.2) +
    geom_vline(xintercept = var_99, 
               color = "#C0392B", 
               linetype = "dashed", 
               linewidth = 1) +
    geom_vline(xintercept = var_95, 
               color = "#E67E22", 
               linetype = "dashed", 
               linewidth = 1) +
    geom_vline(xintercept = media, 
               color = "#27AE60", 
               linetype = "solid", 
               linewidth = 1.2) +
    annotate("text", x = var_99, y = Inf, 
             label = paste0("VaR 99%\n$", round(var_99, 1), "M"), 
             vjust = 1.5, hjust = -0.1, size = 3) +
    annotate("text", x = var_95, y = Inf, 
             label = paste0("VaR 95%\n$", round(var_95, 1), "M"), 
             vjust = 1.5, hjust = -0.1, size = 3) +
    annotate("text", x = media, y = Inf, 
             label = paste0("Media\n$", round(media, 1), "M"), 
             vjust = 1.5, hjust = 1.1, size = 3, color = "#27AE60") +
    scale_x_continuous(labels = scales::dollar_format(suffix = "M", prefix = "$")) +
    labs(
      title = paste("Distribución de Valor del Portafolio -", horizonte_nombre),
      x = "Valor del Portafolio (Millones USD)",
      y = "Densidad"
    ) +
    theme_minimal(base_size = 11) +
    theme(
      plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
      panel.grid.minor = element_blank()
    )

  return(p)
}

# Crear gráficos para horizontes clave
p_6m <- crear_histograma_var("6 Meses", 126/63)
p_1y <- crear_histograma_var("1 Año", 252/63)
p_2y <- crear_histograma_var("2 Años", 504/63)
p_10y <- crear_histograma_var("10 Años", 2520/63)

plots_var <- list(p_6m, p_1y, p_2y, p_10y)
plots_validos <- Filter(Negate(is.null), plots_var)

if (length(plots_validos) > 0) {
gridExtra::grid.arrange(grobs = plots_validos, ncol = 2,
top = "Figura 4. Distribuciones de Valor del Portafolio y VaR en Múltiples Horizontes")
} else {
cat("No hay gráficos disponibles para los horizontes seleccionados.\n")
}

7.4 Expected Shortfall (CVaR)

El Expected Shortfall, también conocido como Conditional Value at Risk (CVaR), mide la pérdida esperada dado que se ha superado el VaR. Es una medida de riesgo coherente (Artzner et al., 1999).

# ============================================================================
# CONDITIONAL VALUE AT RISK (Expected Shortfall)
# ============================================================================

calcular_cvar <- function(valores, nivel_confianza, inversion) {
  var <- quantile(valores, 1 - nivel_confianza)
  valores_cola <- valores[valores >= var]
  cvar <- mean(valores_cola)
  perdida_esperada <- inversion - cvar

  return(list(
    cvar = cvar,
    perdida_esperada = perdida_esperada,
    n_escenarios_cola = length(valores_cola),
    pct_escenarios = (length(valores_cola) / length(valores)) * 100
  ))
}

# Calcular CVaR para horizonte de 10 años
valores_10y <- valor_portafolio_sim[40, ]

cvar_99 <- calcular_cvar(valores_10y, 0.99, inversion_inicial)
cvar_95 <- calcular_cvar(valores_10y, 0.95, inversion_inicial)

# Tabla comparativa VaR vs CVaR
tabla_var_cvar <- tibble(
  Medida = c("VaR 99%", "CVaR 99%", "VaR 95%", "CVaR 95%"),
  "Valor ($)" = c(
    quantile(valores_10y, 0.01),
    cvar_99$cvar,
    quantile(valores_10y, 0.05),
    cvar_95$cvar
  ),
  "Pérdida Esperada ($)" = c(
    inversion_inicial - quantile(valores_10y, 0.01),
    cvar_99$perdida_esperada,
    inversion_inicial - quantile(valores_10y, 0.05),
    cvar_95$perdida_esperada
  ),
  "Pérdida (%)" = c(
    (inversion_inicial - quantile(valores_10y, 0.01)) / inversion_inicial * 100,
    (cvar_99$perdida_esperada / inversion_inicial) * 100,
    (inversion_inicial - quantile(valores_10y, 0.05)) / inversion_inicial * 100,
    (cvar_95$perdida_esperada / inversion_inicial) * 100
  ),
  "Interpretación" = c(
    "Pérdida máxima con 99% confianza",
    "Pérdida promedio si se supera VaR 99%",
    "Pérdida máxima con 95% confianza",
    "Pérdida promedio si se supera VaR 95%"
  )
)

tabla_var_cvar %>%
  gt() %>%
  tab_header(
    title = "Tabla 12. VaR vs Expected Shortfall (CVaR) - Horizonte 10 Años",
    subtitle = "Comparación de medidas de riesgo de cola"
  ) %>%
  fmt_currency(columns = 2:3, currency = "USD", decimals = 0) %>%
  fmt_number(columns = 4, decimals = 2) %>%
  opt_align_table_header(align = "center") %>%
  tab_style(
    style = cell_fill(color = "#FFF3E0"),
    locations = cells_body(rows = c(2, 4))
  ) %>%
  tab_source_note(
    source_note = md("*Nota:* El CVaR es siempre mayor o igual que el VaR, capturando el riesgo extremo en la cola de la distribución.")
  )
Tabla 12. VaR vs Expected Shortfall (CVaR) - Horizonte 10 Años
Comparación de medidas de riesgo de cola
Medida Valor ($) Pérdida Esperada ($) Pérdida (%) Interpretación
VaR 99% $9,090,950 $909,050 9.09 Pérdida máxima con 99% confianza
CVaR 99% $10,501,575 −$501,575 −5.02 Pérdida promedio si se supera VaR 99%
VaR 95% $9,526,510 $473,490 4.73 Pérdida máxima con 95% confianza
CVaR 95% $10,550,829 −$550,829 −5.51 Pérdida promedio si se supera VaR 95%
Nota: El CVaR es siempre mayor o igual que el VaR, capturando el riesgo extremo en la cola de la distribución.

Análisis: El Expected Shortfall proporciona información adicional valiosa sobre las pérdidas en escenarios extremos. Observamos que el CVaR es consistentemente mayor que el VaR, reflejando que las pérdidas condicionales en la cola son significativamente más severas. Esto refuerza la necesidad de estrategias de cobertura robustas.


8 Árboles Binomiales para Valuación de Opciones

8.1 Marco Teórico Completo

El modelo binomial de Cox, Ross y Rubinstein (1979) constituye uno de los pilares de la teoría moderna de valuación de derivados. Este modelo discretiza el tiempo y asume que en cada período el precio del activo subyacente puede moverse hacia arriba (factor \(u\)) o hacia abajo (factor \(d\)) con probabilidades ajustadas al riesgo.

8.1.1 Especificación del Modelo para 8 Pasos Trimestrales

Para un horizonte de 2 años con liquidación trimestral:

  • Número de pasos: \(n = 8\) (trimestres)
  • Incremento temporal: \(\Delta t = 0.25\) años (3 meses)
  • Factor de subida: \(u = e^{\sigma\sqrt{\Delta t}}\)
  • Factor de bajada: \(d = e^{-\sigma\sqrt{\Delta t}} = \frac{1}{u}\)
  • Probabilidad neutral al riesgo: \(p = \frac{e^{r\Delta t} - d}{u - d}\)

8.1.2 Diferencias entre Opciones Europeas y Americanas

Opciones Europeas: Solo pueden ejercerse en la fecha de vencimiento. Su valor en cada nodo se calcula como:

\[ f_{i,j} = e^{-r\Delta t}[p \cdot f_{i+1,j} + (1-p) \cdot f_{i+1,j+1}] \]

Opciones Americanas: Pueden ejercerse en cualquier momento hasta el vencimiento. En cada nodo se compara:

\[ f_{i,j} = \max\left(\text{valor intrínseco}, e^{-r\Delta t}[p \cdot f_{i+1,j} + (1-p) \cdot f_{i+1,j+1}]\right) \]

Donde el valor intrínseco es: - Call: \(\max(S_{i,j} - K, 0)\) - Put: \(\max(K - S_{i,j}, 0)\)

8.2 Determinación de Strike

##   BAJO: $270, $900, $180 
##   MEDIO: $290, $920, $190 
##   ALTO: $310, $950, $200

La selección de los precios de ejercicio para cada escenario se realizó conforme tres criterios técnicos fundamentales. En primer lugar, la liquidez de cada contrato se verificó mediante el análisis del interés abierto y el diferencial bid-ask, asegurando que fueran strikes con alta negociación y facilidad en la liquidación de posiciones (Finance, 2025; Hull, 2018). En segundo término, se seleccionaron strikes próximos al precio spot, garantizando la condición at the money (ATM) para el escenario medio, así como valores out of the money (OTM) y in the money (ITM) para los escenarios bajo y alto, respectivamente. Por último, se consideró la vigencia del contrato, ajustando los strikes a la fecha de vencimiento solicitada (enero de 2028) para asegurar coherencia con el horizonte temporal de la inversión y la cobertura (Finance, 2025; Hull, 2018; Markowitz, 1952). De este modo, se fundamenta la elección óptima para la operación de derivados sobre cada activo, maximizando la eficiencia y cobertura del portafolio.

Para TSM, el strike de 270 USD fue seleccionado para el escenario bajo al ser OTM, presentando un diferencial bid-ask bajo y una concentración de open interest que permite asegurar la liquidez requerida para la cobertura. El strike de 290 USD se estableció para el escenario medio, buscando la condición ATM más cercana al spot, donde se observa un volumen relevante y parámetros de volatilidad implícita representativos. Finalmente, el strike de 310 USD se eligió para el escenario alto por estar ligeramente ITM, mostrando también interés abierto y spreads reducidos, facilitando así una ejecución eficiente para un portafolio de cobertura más agresiva (Finance, 2025).

Para COST, el strike de 900 USD se emplea en el escenario bajo por su naturaleza OTM, además de ser uno de los strikes más transados y poseer un diferencial bid-ask reducido, lo que denota liquidez. El strike de 920 USD se ajusta al escenario medio como ATM, cercano al precio spot, optimizando el balance entre prima pagada y protección ofrecida. Para el escenario alto, el strike de 950 USD fue elegido por situarse ligeramente ITM y mantener buen interés abierto, asegurando cobertura eficiente y facilidad de ejecución en el mercado de derivados (Finance, 2025).

Para JNJ, el strike de 180 USD corresponde al escenario bajo por ubicarse ligeramente OTM y presentar señal de liquidez adecuada, atendiendo el volumen e interés abierto. El strike de 190 USD, alineado bajo el criterio ATM, ofrece el mayor open interest y menor diferencial bid-ask en la tabla de opciones, optimizando la cobertura respecto al spot. Finalmente, el strike de 200 USD, seleccionado para el escenario alto, revela condiciones ITM y mantiene liquidez, resultando apropiado para cobertura avanzada mediante opciones con mayor probabilidad de ejercicio (Finance, 2025).

La metodología utilizada tiene sustento en los principios de la teoría moderna de portafolios y la gestión de riesgos con derivados, tal como se establece por Markowitz y Hull. De este modo, se maximiza el índice de cobertura ajustado por liquidez y preferencia de mercado en beneficio de la protección integral del portafolio (Hull, 2018; Markowitz, 1952).

8.3 Resumen de Valuación

# ============================================================================
# FUNCIÓN PARA VALUAR TODAS LAS OPCIONES SISTEMÁTICAMENTE
# ============================================================================

valuar_todas_opciones_sistematico <- function() {

  cat("\n╔════════════════════════════════════════════════════════╗\n")
  cat("║          INICIANDO VALUACIÓN DE 72 ÁRBOLES             ║\n")
  cat("╚════════════════════════════════════════════════════════╝\n\n")

  # Contenedor de resultados
  resultados_totales <- list()
  arboles_totales <- list()
  contador <- 0
  tiempo_inicio <- Sys.time()

  # Iterar sobre escenarios
  for(escenario in c("BAJO", "MEDIO", "ALTO")) {

    cat("\n", rep("═", 60), "\n", sep = "")
    cat("  ESCENARIO:", escenario, "\n")
    cat(rep("═", 60), "\n\n", sep = "")

    strikes_esc <- strikes_por_escenario[[escenario]]
    vol_impl_esc <- vol_impl_por_escenario[[escenario]]

    # Iterar sobre activos
    for(i_activo in 1:length(activos)) {

      activo <- activos[i_activo]
      S0 <- precios_actuales[i_activo]
      K <- strikes_esc[i_activo]

      cat(sprintf("  → %s: S0 = %s | K = %s\n", 
                  activo, 
                  scales::dollar(S0), 
                  scales::dollar(K)))

      # Iterar sobre tipos de volatilidad
      for(vol_tipo in tipos_volatilidad) {

        sigma <- if(vol_tipo == "Implícita") {
          vol_impl_esc[i_activo]
        } else {
          vol_garch_valores[i_activo]
        }

        # Iterar sobre tipos de opción
        for(tipo_op in tipos_opciones) {
          for(tipo_ej in tipos_ejercicio) {

            contador <- contador + 1

            # Valuar opción
            arbol <- calcular_arbol_8_pasos(
              S0 = S0,
              K = K,
              r = tasa_libre_riesgo,
              sigma = sigma,
              tipo_opcion = tipo_op,
              tipo_ejercicio = tipo_ej,
              tipo_volatilidad = vol_tipo
            )

            # Nombre único
            nombre <- paste(escenario, activo, tipo_ej, tipo_op, vol_tipo, sep = "_")

            # Guardar resultados
            resultados_totales[[nombre]] <- list(
              escenario = escenario,
              activo = activo,
              ticker = activo,
              tipo_opcion = tipo_op,
              tipo_ejercicio = tipo_ej,
              tipo_volatilidad = vol_tipo,
              S0 = S0,
              K = K,
              sigma = sigma,
              precio = arbol$precio_opcion,
              delta = arbol$delta,
              gamma = arbol$gamma,
              theta = arbol$theta,
              n_ejercicios_anticipados = length(arbol$ejercicios_anticipados)
            )

            arboles_totales[[nombre]] <- arbol

            # Progreso
            if(contador %% 12 == 0) {
              cat(sprintf("    [%2d/72] Completado\n", contador))
            }
          }
        }
      }
    }
  }

  tiempo_fin <- Sys.time()
  tiempo_total <- as.numeric(difftime(tiempo_fin, tiempo_inicio, units = "secs"))

  cat("\n", rep("═", 60), "\n", sep = "")
  cat("  ✓ VALUACIÓN COMPLETADA EXITOSAMENTE\n")
  cat("  • Total de árboles generados: 72\n")
  cat("  • Tiempo de ejecución:", round(tiempo_total, 2), "segundos\n")
  cat(rep("═", 60), "\n\n", sep = "")

  return(list(
    resultados = resultados_totales,
    arboles = arboles_totales,
    tiempo_ejecucion = tiempo_total
  ))
}

# EJECUTAR VALUACIÓN COMPLETA
set.seed(123)
valuacion_completa <- valuar_todas_opciones_sistematico()
## 
## ╔════════════════════════════════════════════════════════╗
## ║          INICIANDO VALUACIÓN DE 72 ÁRBOLES             ║
## ╚════════════════════════════════════════════════════════╝
## 
## 
## ════════════════════════════════════════════════════════════
##   ESCENARIO: BAJO 
## ════════════════════════════════════════════════════════════
## 
##   → TSM: S0 = $289.24 | K = $270
##   → COST: S0 = $923.58 | K = $900
##     [12/72] Completado
##   → JNJ: S0 = $186.97 | K = $180
##     [24/72] Completado
## 
## ════════════════════════════════════════════════════════════
##   ESCENARIO: MEDIO 
## ════════════════════════════════════════════════════════════
## 
##   → TSM: S0 = $289.24 | K = $290
##   → COST: S0 = $923.58 | K = $920
##     [36/72] Completado
##   → JNJ: S0 = $186.97 | K = $190
##     [48/72] Completado
## 
## ════════════════════════════════════════════════════════════
##   ESCENARIO: ALTO 
## ════════════════════════════════════════════════════════════
## 
##   → TSM: S0 = $289.24 | K = $310
##   → COST: S0 = $923.58 | K = $950
##     [60/72] Completado
##   → JNJ: S0 = $186.97 | K = $200
##     [72/72] Completado
## 
## ════════════════════════════════════════════════════════════
##   ✓ VALUACIÓN COMPLETADA EXITOSAMENTE
##   • Total de árboles generados: 72
##   • Tiempo de ejecución: 0.18 segundos
## ════════════════════════════════════════════════════════════

8.4 Tabla Maestra de Resultados

# ============================================================================
# COMPILAR TODOS LOS RESULTADOS EN TABLA MAESTRA
# ============================================================================

# Convertir resultados a data frame
df_72_arboles <- do.call(rbind, lapply(names(valuacion_completa$resultados), function(nombre) {
  res <- valuacion_completa$resultados[[nombre]]
  data.frame(
    ID = nombre,
    Escenario = res$escenario,
    Activo = res$activo,
    "Tipo Opción" = res$tipo_opcion,
    Ejercicio = res$tipo_ejercicio,
    Volatilidad = res$tipo_volatilidad,
    "S0 ($)" = res$S0,
    "K ($)" = res$K,
    "σ (%)" = res$sigma * 100,
    "Precio ($)" = res$precio,
    "Delta" = res$delta,
    "Gamma" = res$gamma,
    "Theta" = res$theta,
    "Moneyness" = res$K / res$S0,
    "Prima/Spot (%)" = (res$precio / res$S0),
    check.names = FALSE,
    stringsAsFactors = FALSE
  )
}))

# Tabla interactiva completa
DT::datatable(
  df_72_arboles,
  rownames = FALSE,
  caption = "Tabla 13. Resultados de Valuación de los 72 Árboles Binomiales",
  filter = 'top',
  options = list(
    pageLength = 25,
    autoWidth = TRUE,
    scrollX = TRUE,
    dom = 'Bfrtip',
    buttons = c('copy', 'csv', 'excel', 'pdf', 'print'),
    columnDefs = list(
      list(className = 'dt-center', targets = '_all')
    )
  ),
  extensions = c('Buttons', 'FixedColumns')
) %>%
  formatCurrency(c("S0 ($)", "K ($)", "Precio ($)"), currency = "$", digits = 4) %>%
  formatPercentage(c("σ (%)", "Prima/Spot (%)"), digits = 2) %>%
  formatRound(c("Delta", "Gamma", "Theta", "Moneyness"), digits = 6) %>%
  formatStyle(
    "Precio ($)",
    background = styleColorBar(range(df_72_arboles$`Precio ($)`), 'lightblue'),
    backgroundSize = '98% 88%',
    backgroundRepeat = 'no-repeat',
    backgroundPosition = 'center'
  ) %>%
  formatStyle(
    "Ejercicio",
    backgroundColor = styleEqual(
      c("europea", "americana"),
      c('#E8F5E9', '#FFF9C4')
    )
  )

8.5 Visualización de Árboles Binomiales

Los 72 árboles binomiales se presentan organizados en pestañas interactivas por escenario de mercado. Cada escenario contiene 24 árboles.

8.5.1 Escenario BAJO

En el escenario BAJO, el portafolio experimenta una trayectoria adversa con caídas significativas. Los árboles binomiales muestran cómo las opciones de cobertura adquieren valor en este contexto.

8.5.1.1 TSM - Taiwan Semiconductors

# ============================================================================
# ÁRBOLES BINOMIALES PARA TAIWAN EN ESCENARIO BAJO
# ============================================================================

library(grid)
# Extraer los 8 árboles de TSM en escenario BAJO
arboles_bajo_TSM <- list(
  call_eu_vimpl = valuacion_completa$arboles[["BAJO_TSM_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["BAJO_TSM_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["BAJO_TSM_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["BAJO_TSM_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["BAJO_TSM_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["BAJO_TSM_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["BAJO_TSM_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["BAJO_TSM_americana_put_GARCH"]]
)

# Generar visualizaciones
graficos_bajo_TSM <- lapply(arboles_bajo_TSM, visualizar_arbol_8pasos)

# Organizar en grilla 4x2
grid.arrange(
  grobs = graficos_bajo_TSM,
  ncol = 2,
  top = textGrob("Figura 5. ESCENARIO BAJO - TAIWAN (TSM) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.1.2 COST - Costco Wholesale Corp.

library(grid)
# Extraer árboles de COST
arboles_bajo_COST <- list(
  call_eu_vimpl = valuacion_completa$arboles[["BAJO_COST_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["BAJO_COST_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["BAJO_COST_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["BAJO_COST_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["BAJO_COST_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["BAJO_COST_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["BAJO_COST_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["BAJO_COST_americana_put_GARCH"]]
)

graficos_bajo_COST <- lapply(arboles_bajo_COST, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_bajo_COST,
  ncol = 2,
  top = textGrob("Figura 14. ESCENARIO BAJO - COSTCO (COST) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.1.3 JNJ - Johnson & Johnson

# Extraer árboles de JNJ
arboles_bajo_JNJ <- list(
  call_eu_vimpl = valuacion_completa$arboles[["BAJO_JNJ_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["BAJO_JNJ_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["BAJO_JNJ_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["BAJO_JNJ_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["BAJO_JNJ_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["BAJO_JNJ_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["BAJO_JNJ_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["BAJO_JNJ_americana_put_GARCH"]]
)

graficos_bajo_JNJ <- lapply(arboles_bajo_JNJ, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_bajo_JNJ,
  ncol = 2,
  top = textGrob("Figura 7. ESCENARIO BAJO - Johnson & Johnson (JNJ) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.2 Escenario MEDIO

El escenario MEDIO representa la trayectoria más probable del portafolio, cercana a la mediana de las simulaciones. Estos árboles muestran precios de opciones en condiciones de mercado típicas.

8.5.2.1 TSM - Taiwan Semiconductors

# Extraer árboles de TSM en escenario MEDIO
arboles_medio_TSM <- list(
  call_eu_vimpl = valuacion_completa$arboles[["MEDIO_TSM_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["MEDIO_TSM_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["MEDIO_TSM_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["MEDIO_TSM_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["MEDIO_TSM_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["MEDIO_TSM_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["MEDIO_TSM_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["MEDIO_TSM_americana_put_GARCH"]]
)

graficos_medio_TSM <- lapply(arboles_medio_TSM, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_medio_TSM,
  ncol = 2,
  top = textGrob("Figura 15. ESCENARIO MEDIO - TAIWAN (TSM) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.2.2 COST - Costco Wholesale Corp.

arboles_medio_COST <- list(
  call_eu_vimpl = valuacion_completa$arboles[["MEDIO_COST_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["MEDIO_COST_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["MEDIO_COST_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["MEDIO_COST_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["MEDIO_COST_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["MEDIO_COST_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["MEDIO_COST_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["MEDIO_COST_americana_put_GARCH"]]
)

graficos_medio_COST <- lapply(arboles_medio_COST, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_medio_COST,
  ncol = 2,
  top = textGrob("Figura 16. ESCENARIO MEDIO - COSTCO (COST) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.2.3 JNJ - Johnson & Johnson

arboles_medio_JNJ <- list(
  call_eu_vimpl = valuacion_completa$arboles[["MEDIO_JNJ_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["MEDIO_JNJ_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["MEDIO_JNJ_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["MEDIO_JNJ_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["MEDIO_JNJ_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["MEDIO_JNJ_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["MEDIO_JNJ_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["MEDIO_JNJ_americana_put_GARCH"]]
)

graficos_medio_JNJ <- lapply(arboles_medio_JNJ, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_medio_JNJ,
  ncol = 2,
  top = textGrob("Figura 17. ESCENARIO MEDIO - Johnson & Johnson (JNJ) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.3 Escenario ALTO

El escenario ALTO representa el mejor desempeño del portafolio entre las simulaciones. En este contexto favorable, las opciones de cobertura pueden no ejercerse, pero su costo sigue siendo relevante.

8.5.3.1 TSM - Taiwan Semiconductors

arboles_alto_TSM <- list(
  call_eu_vimpl = valuacion_completa$arboles[["ALTO_TSM_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["ALTO_TSM_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["ALTO_TSM_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["ALTO_TSM_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["ALTO_TSM_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["ALTO_TSM_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["ALTO_TSM_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["ALTO_TSM_americana_put_GARCH"]]
)

graficos_alto_TSM <- lapply(arboles_alto_TSM, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_alto_TSM,
  ncol = 2,
  top = textGrob("Figura 18. ESCENARIO ALTO - TAIWAN (TSM) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.3.2 COST - Costco Wholesale Corp.

arboles_alto_COST <- list(
  call_eu_vimpl = valuacion_completa$arboles[["ALTO_COST_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["ALTO_COST_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["ALTO_COST_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["ALTO_COST_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["ALTO_COST_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["ALTO_COST_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["ALTO_COST_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["ALTO_COST_americana_put_GARCH"]]
)

graficos_alto_COST <- lapply(arboles_alto_COST, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_alto_COST,
  ncol = 2,
  top = textGrob("Figura 19. ESCENARIO ALTO - COSTCO (COST) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.3.3 JNJ - Johnson & Johnson

arboles_alto_JNJ <- list(
  call_eu_vimpl = valuacion_completa$arboles[["ALTO_JNJ_europea_call_Implícita"]],
  put_eu_vimpl = valuacion_completa$arboles[["ALTO_JNJ_europea_put_Implícita"]],
  call_am_vimpl = valuacion_completa$arboles[["ALTO_JNJ_americana_call_Implícita"]],
  put_am_vimpl = valuacion_completa$arboles[["ALTO_JNJ_americana_put_Implícita"]],
  call_eu_garch = valuacion_completa$arboles[["ALTO_JNJ_europea_call_GARCH"]],
  put_eu_garch = valuacion_completa$arboles[["ALTO_JNJ_europea_put_GARCH"]],
  call_am_garch = valuacion_completa$arboles[["ALTO_JNJ_americana_call_GARCH"]],
  put_am_garch = valuacion_completa$arboles[["ALTO_JNJ_americana_put_GARCH"]]
)

graficos_alto_JNJ <- lapply(arboles_alto_JNJ, visualizar_arbol_8pasos)

grid.arrange(
  grobs = graficos_alto_JNJ,
  ncol = 2,
  top = textGrob("Figura 13. ESCENARIO ALTO - Johnson & Johnson (JNJ) - 8 Configuraciones de Opciones",
                 gp = gpar(fontsize = 14, fontface = "bold", col = "#2c3e50"))
)

8.5.4 Resumen Comparativo

# ============================================================================
# TABLA RESUMEN DE TODOS LOS ÁRBOLES BINOMIALES
# ============================================================================

# Crear tabla resumen con información clave de los 72 árboles
tabla_resumen_arboles <- df_72_arboles %>%
  arrange(Escenario, Activo, Ejercicio, `Tipo Opción`, Volatilidad) %>%
  select(Escenario, Activo, `Tipo Opción`, Ejercicio, Volatilidad, 
         `Precio ($)`, Delta, `Prima/Spot (%)`)

# Tabla interactiva con filtros
DT::datatable(
  tabla_resumen_arboles,
  rownames = FALSE,
  caption = "Tabla 14. Resumen: 72 Árboles Binomiales Completos",
  filter = 'top',
  extensions = c('Buttons', 'FixedColumns'),
  options = list(
    pageLength = 25,
    autoWidth = TRUE,
    scrollX = TRUE,
    dom = 'Bfrtip',
    buttons = c('copy', 'csv', 'excel', 'pdf'),
    columnDefs = list(
      list(className = 'dt-center', targets = '_all')
    )
  )
) %>%
  formatCurrency("Precio ($)", currency = "$", digits = 4) %>%
  formatRound("Delta", digits = 4) %>%
  formatPercentage("Prima/Spot (%)", digits = 2) %>%
  formatStyle(
    "Escenario",
    backgroundColor = styleEqual(
      c("BAJO", "MEDIO", "ALTO"),
      c('#FFCDD2', '#FFF9C4', '#E8F5E9')
    )
  ) %>%
  formatStyle(
    "Ejercicio",
    backgroundColor = styleEqual(
      c("europea", "americana"),
      c('#E1F5FE', '#FFF8E1')
    )
  )

8.5.4.1 Estadísticas por Escenario

# Calcular estadísticas descriptivas por escenario
stats_por_escenario <- df_72_arboles %>%
  group_by(Escenario) %>%
  summarise(
    "Precio Promedio ($)" = mean(`Precio ($)`),
    "Precio Mínimo ($)" = min(`Precio ($)`),
    "Precio Máximo ($)" = max(`Precio ($)`),
    "Desv. Estándar ($)" = sd(`Precio ($)`),
    "Delta Promedio" = mean(Delta),
    "Prima/Spot Promedio (%)" = mean(`Prima/Spot (%)`)
  )

stats_por_escenario %>%
  gt() %>%
  tab_header(
    title = "Tabla 15. Estadísticas Descriptivas por Escenario",
    subtitle = "Resumen agregado de los 24 árboles por escenario"
  ) %>%
  fmt_currency(columns = 2:5, currency = "USD", decimals = 4) %>%
  fmt_number(columns = 6:7, decimals = 4) %>%
  opt_align_table_header(align = "center") %>%
  data_color(
    columns = 2,
    colors = scales::col_numeric(
      palette = c("#E8F5E9", "#FFF9C4", "#FFCDD2"),
      domain = NULL
    )
  )
Tabla 15. Estadísticas Descriptivas por Escenario
Resumen agregado de los 24 árboles por escenario
Escenario Precio Promedio ($) Precio Mínimo ($) Precio Máximo ($) Desv. Estándar ($) Delta Promedio Prima/Spot Promedio (%)
ALTO $32.7832 $5.5644 $62.1345 $19.0993 −0.0545 0.0772
BAJO $33.6713 $4.7514 $79.0336 $24.2661 0.1656 0.0778
MEDIO $31.3010 $8.7219 $64.5901 $19.4004 0.0627 0.0720

8.5.5 Exportar a PDF

Para exportar todos los 72 árboles a un documento PDF, ejecuta el siguiente código:

# ============================================================================
# CÓDIGO PARA EXPORTAR TODOS LOS 72 ÁRBOLES A UN PDF
# ============================================================================

library(gridExtra)
library(grid)

pdf("Todos_los_72_Arboles_Binomiales_Completo.pdf", width = 16, height = 10)

escenarios_orden <- c("BAJO", "MEDIO", "ALTO")

for(escenario in escenarios_orden) {

  # Página de título para el escenario
  grid.newpage()
  grid.text(
    paste("ESCENARIO:", escenario),
    x = 0.5, y = 0.5,
    gp = gpar(fontsize = 30, fontface = "bold", col = "#2c3e50")
  )
  grid.text(
    paste("24 árboles binomiales | 3 activos × 4 opciones × 2 volatilidades"),
    x = 0.5, y = 0.4,
    gp = gpar(fontsize = 16, col = "#7f8c8d")
  )

  # Para cada activo en este escenario
  for(activo in activos) {

    # Extraer los 8 árboles
    arboles_activo <- list(
      call_eu_vimpl = valuacion_completa$arboles[[paste(escenario, activo, "europea", "call", "Implícita", sep = "_")]],
      put_eu_vimpl = valuacion_completa$arboles[[paste(escenario, activo, "europea", "put", "Implícita", sep = "_")]],
      call_am_vimpl = valuacion_completa$arboles[[paste(escenario, activo, "americana", "call", "Implícita", sep = "_")]],
      put_am_vimpl = valuacion_completa$arboles[[paste(escenario, activo, "americana", "put", "Implícita", sep = "_")]],
      call_eu_garch = valuacion_completa$arboles[[paste(escenario, activo, "europea", "call", "GARCH", sep = "_")]],
      put_eu_garch = valuacion_completa$arboles[[paste(escenario, activo, "europea", "put", "GARCH", sep = "_")]],
      call_am_garch = valuacion_completa$arboles[[paste(escenario, activo, "americana", "call", "GARCH", sep = "_")]],
      put_am_garch = valuacion_completa$arboles[[paste(escenario, activo, "americana", "put", "GARCH", sep = "_")]]
    )

    # Generar gráficos
    graficos <- lapply(arboles_activo, visualizar_arbol_8pasos)

    # Organizar en grilla (4x2)
    grid.arrange(
      grobs = graficos,
      ncol = 2,
      top = textGrob(
        paste("Escenario", escenario, "-", activo, "- Todos los Tipos de Opciones"),
        gp = gpar(fontsize = 14, fontface = "bold")
      )
    )
  }
}

dev.off()

cat("\n✓ PDF generado: 'Todos_los_72_Arboles_Binomiales_Completo.pdf'\n")
cat("  El archivo contiene los 72 árboles organizados por escenario y activo.\n")

Nota metodológica: Los árboles binomiales muestran la evolución del precio del subyacente (números azules) y el valor de la opción (números morados) en cada nodo. Los árboles americanos indican nodos de ejercicio anticipado óptimo cuando aplica.


9 Selección de Estrategia de Cobertura Óptima por Escenario

9.1 Marco Teórico: Estrategias Sintéticas y Equivalencias de Posiciones

Las estrategias de cobertura con opciones se fundamentan en el principio de paridad put-call y las equivalencias sintéticas entre posiciones. Estas relaciones permiten construir perfiles de payoff específicos mediante combinaciones de acciones y opciones.

9.1.1 Estrategias Fundamentales

1. Protective Put (Largo Acción + Largo Put) \[ \text{Payoff} = \max(S_T, K) - P \]

Esta estrategia proporciona protección de piso (downside protection) al portafolio. El put actúa como un seguro, limitando pérdidas al nivel del strike. Es sintéticamente equivalente a una call larga.

2. Covered Call (Largo Acción + Corto Call) \[ \text{Payoff} = \min(S_T, K) + C \]

Genera ingreso adicional mediante la prima del call vendido, sacrificando potencial alcista. Es sintéticamente equivalente a una put corta.

3. Put Spread (Largo Put \(K_{long}\) + Corto Put \(K_{short}\))

\[ \text{Payoff} = \max(K_{long} − S_T, 0) − \max(K_{short} − S_T, 0) − Debit \]

crea un rango de protección entre \(K_{long}\) y \(K_{short}\) con costo inicial acotado al débito pagado.

9.1.2 Criterios de Selección por Escenario

La selección de estrategia depende de:

  1. Expectativa direccional del mercado (escenario BAJO/MEDIO/ALTO)
  2. Volatilidad implícita vs histórica (GARCH)
  3. Costo de cobertura relativo al capital
  4. Ratio de eficiencia: Protección / Costo
  5. Delta del portafolio y sensibilidad al subyacente
# ============================================================================
# ANÁLISIS PRELIMINAR PARA SELECCIÓN DE ESTRATEGIA
# ============================================================================

# Extraer características de cada escenario
analisis_escenarios <- list(
  BAJO = list(
    valor_portafolio = quantile(valor_portafolio_sim[nrow(valor_portafolio_sim), ], 0.10),
    retorno_esperado = "Negativo (-20% a -40%)",
    riesgo = "Muy Alto",
    objetivo = "Protección máxima contra caídas",
    estrategia_recomendada = "Protective Put (Largo Acción + Largo Put Americana)"
  ),
  MEDIO = list(
    valor_portafolio = median(valor_portafolio_sim[nrow(valor_portafolio_sim), ]),
    retorno_esperado = "Moderado (0% a +15%)",
    riesgo = "Medio",
    objetivo = "Balance entre protección y costo",
    estrategia_recomendada = "PutSpread (Put OTM + Call OTM vendido)"
  ),
  ALTO = list(
    valor_portafolio = quantile(valor_portafolio_sim[nrow(valor_portafolio_sim), ], 0.90),
    retorno_esperado = "Alto (+25% a +50%)",
    riesgo = "Bajo",
    objetivo = "Generar ingreso adicional",
    estrategia_recomendada = "Covered Call (Venta de Calls OTM)"
  )
)

# Mostrar resumen
for(esc in names(analisis_escenarios)) {
  cat(sprintf("\n▼ ESCENARIO %s:\n", esc))
  cat(sprintf("  Valor Portafolio: %s\n", 
              scales::dollar(analisis_escenarios[[esc]]$valor_portafolio)))
  cat(sprintf("  Retorno Esperado: %s\n", 
              analisis_escenarios[[esc]]$retorno_esperado))
  cat(sprintf("  Riesgo: %s\n", 
              analisis_escenarios[[esc]]$riesgo))
  cat(sprintf("  Objetivo: %s\n", 
              analisis_escenarios[[esc]]$objetivo))
  cat(sprintf("  ✓ Estrategia: %s\n", 
              analisis_escenarios[[esc]]$estrategia_recomendada))
}
## 
## ▼ ESCENARIO BAJO:
##   Valor Portafolio: $9,735,880
##   Retorno Esperado: Negativo (-20% a -40%)
##   Riesgo: Muy Alto
##   Objetivo: Protección máxima contra caídas
##   ✓ Estrategia: Protective Put (Largo Acción + Largo Put Americana)
## 
## ▼ ESCENARIO MEDIO:
##   Valor Portafolio: $10,423,259
##   Retorno Esperado: Moderado (0% a +15%)
##   Riesgo: Medio
##   Objetivo: Balance entre protección y costo
##   ✓ Estrategia: PutSpread (Put OTM + Call OTM vendido)
## 
## ▼ ESCENARIO ALTO:
##   Valor Portafolio: $11,335,696
##   Retorno Esperado: Alto (+25% a +50%)
##   Riesgo: Bajo
##   Objetivo: Generar ingreso adicional
##   ✓ Estrategia: Covered Call (Venta de Calls OTM)

9.2 Selección de Opciones Óptimas por Escenario

Análisis exhaustivo de los 72 árboles binomiales para identificar las opciones más eficientes.

## 
## Analizando TSM en escenario BAJO...
##   ✓ Protective Put: K=$270.00 | Prima=$18.8259
## 
## Analizando COST en escenario BAJO...
##   ✓ Protective Put: K=$900.00 | Prima=$36.7808
## 
## Analizando JNJ en escenario BAJO...
##   ✓ Protective Put: K=$180.00 | Prima=$4.9141
## 
## Analizando TSM en escenario MEDIO...
##   ✓ Put Spread: Long K=$290.00 | Short K=$290.00 | Débito≈$-3.2583
## 
## Analizando COST en escenario MEDIO...
##   ✓ Put Spread: Long K=$920.00 | Short K=$920.00 | Débito≈$-0.3809
## 
## Analizando JNJ en escenario MEDIO...
##   ✓ Put Spread: Long K=$190.00 | Short K=$190.00 | Débito≈$0.0000
## 
## Analizando TSM en escenario ALTO...
##   ✓ Covered Call: K=$310.00 | Prima=$23.2319
## 
## Analizando COST en escenario ALTO...
##   ✓ Covered Call: K=$950.00 | Prima=$48.1675
## 
## Analizando JNJ en escenario ALTO...
##   ✓ Covered Call: K=$200.00 | Prima=$7.1685
## 
## ✓ Análisis de eficiencia completado
##   Mejores opciones identificadas para cada escenario y activo

9.2.1 Tabla Resumen: Opciones Seleccionadas

# ============================================================================
# TABLA DE OPCIONES SELECCIONADAS POR ESTRATEGIA
# ============================================================================

# Compilar resultados en tabla con validación
tabla_seleccion <- data.frame()

for(esc in names(mejores_opciones)) {
  for(act in names(mejores_opciones[[esc]])) {

    estrategia <- mejores_opciones[[esc]][[act]]

    # Validar que existe opcion_principal
    if(is.null(estrategia$opcion_principal) || nrow(estrategia$opcion_principal) == 0) {
      warning(sprintf("No hay opción principal para %s en %s", act, esc))
      next
    }

    op_principal <- estrategia$opcion_principal

    # Crear fila con manejo seguro de valores
    fila <- data.frame(
      Escenario = esc,
      Activo = act,
      Estrategia = estrategia$tipo,
      "Opción Principal" = paste(
        op_principal$Ejercicio[1], 
        op_principal$`Tipo Opción`[1],
        op_principal$Volatilidad[1]
      ),
      "Strike ($)" = op_principal$`K ($)`[1],
      "Prima ($)" = op_principal$`Precio ($)`[1],
      "Delta" = op_principal$Delta[1],
      "Moneyness" = op_principal$Moneyness[1],
      check.names = FALSE,
      stringsAsFactors = FALSE
    )

    tabla_seleccion <- rbind(tabla_seleccion, fila)
  }
}

# Verificar si hay datos
if(nrow(tabla_seleccion) == 0) {
  cat("\n⚠️  ADVERTENCIA: No se pudieron seleccionar opciones.\n")
  cat("   Verifica que df_72_arboles tenga datos correctos.\n\n")

  # Mostrar estructura de df_72_arboles para debug
  cat("\nEstructura de df_72_arboles:\n")
  print(str(df_72_arboles))
  cat("\nPrimeras filas:\n")
  print(head(df_72_arboles))

} else {
  # Tabla interactiva
  DT::datatable(
    tabla_seleccion,
    rownames = FALSE,
    caption = "Tabla 16. Opciones Seleccionadas para Estrategias de Cobertura por Escenario",
    options = list(
      pageLength = 15,
      autoWidth = TRUE,
      dom = 'Bfrtip',
      buttons = c('copy', 'csv', 'excel')
    ),
    extensions = 'Buttons'
  ) %>%
    formatCurrency(c("Strike ($)", "Prima ($)"), currency = "$", digits = 4) %>%
    formatRound(c("Delta", "Moneyness"), digits = 4) %>%
    formatStyle(
      "Estrategia",
      backgroundColor = styleEqual(
        c("Protective Put", "PutSpread", "Covered Call"),
        c('#FFCDD2', '#FFF9C4', '#E8F5E9')
      )
    )
}

9.3 Implementación de Estrategias por Escenario

9.3.1 Estrategia 1: PROTECTIVE PUT para Escenario BAJO

Objetivo: Protección máxima contra caídas severas del mercado.

Fundamento: En el escenario BAJO, el portafolio enfrenta pérdidas significativas. La compra de puts americanas OTM proporciona un piso de protección, limitando pérdidas máximas al nivel del strike menos la prima pagada.

# ============================================================================
# ESTRATEGIA PROTECTIVE PUT - ESCENARIO BAJO
# ============================================================================

# Verificar que existen las opciones seleccionadas
if(!"BAJO" %in% names(mejores_opciones) || length(mejores_opciones[["BAJO"]]) == 0) {
  cat("⚠️  No hay opciones seleccionadas para escenario BAJO.\n")
  cat("   Saltando implementación de estrategia.\n\n")
} else {

  # Parámetros
  capital_cobertura_bajo <- monto_a_cubrir  # 85% del portafolio

  # Calcular número de contratos y costos por activo
  estrategia_bajo_detalle <- list()

  for(i in 1:length(activos)) {
    activo <- activos[i]

    # Verificar que existe la opción
    if(!activo %in% names(mejores_opciones[["BAJO"]])) {
      cat(sprintf("⚠️  No hay opción para %s en BAJO\n", activo))
      next
    }

    # Extraer opción seleccionada
    opcion <- mejores_opciones[["BAJO"]][[activo]]$opcion_principal

    if(is.null(opcion) || nrow(opcion) == 0) {
      cat(sprintf("⚠️  Opción vacía para %s\n", activo))
      next
    }

    # Número de acciones a cubrir
    num_acciones_activo <- num_acciones[i]

    # Número de contratos (1 contrato = 100 acciones)
    num_contratos <- floor(num_acciones_activo / 100)

    # Costo de primas
    costo_prima_total <- num_contratos * 100 * opcion$`Precio ($)`[1]

    # Strike y protección
    strike <- opcion$`K ($)`[1]
    valor_protegido <- num_contratos * 100 * strike

    # Cálculo de financiamiento con apalancamiento
    tasa_trimestral <- tasa_libre_riesgo / 4
    n_periodos <- 8  # 2 años trimestral

    # Intereses totales (simple para claridad)
    costo_intereses <- costo_prima_total * tasa_trimestral * n_periodos
    costo_total_con_intereses <- costo_prima_total + costo_intereses

    # Almacenar
    estrategia_bajo_detalle[[activo]] <- list(
      num_acciones = num_acciones_activo,
      num_contratos = num_contratos,
      strike = strike,
      prima_unitaria = opcion$`Precio ($)`[1],
      costo_prima_total = costo_prima_total,
      costo_intereses = costo_intereses,
      costo_total = costo_total_con_intereses,
      valor_protegido = valor_protegido,
      delta = opcion$Delta[1]
    )

    cat(sprintf("\n► %s (%s):\n", nombres_activos[i], activo))
    cat(sprintf("  Acciones: %s | Contratos: %d\n", 
                scales::comma(num_acciones_activo), num_contratos))
    cat(sprintf("  Strike: %s | Prima: %s\n", 
                scales::dollar(strike), scales::dollar(opcion$`Precio ($)`[1])))
    cat(sprintf("  Costo Prima: %s\n", 
                scales::dollar(costo_prima_total)))
    cat(sprintf("  Costo Intereses (2Y): %s\n", 
                scales::dollar(costo_intereses)))
    cat(sprintf("  COSTO TOTAL: %s\n", 
                scales::dollar(costo_total_con_intereses)))
    cat(sprintf("  Valor Protegido: %s\n", 
                scales::dollar(valor_protegido)))
  }

  # Totales de la estrategia
  if(length(estrategia_bajo_detalle) > 0) {
    costo_total_estrategia_bajo <- sum(sapply(estrategia_bajo_detalle, function(x) x$costo_total))
    valor_total_protegido_bajo <- sum(sapply(estrategia_bajo_detalle, function(x) x$valor_protegido))

    cat("\n", rep("═", 60), "\n", sep = "")
    cat(sprintf("COSTO TOTAL ESTRATEGIA BAJO: %s\n", 
                scales::dollar(costo_total_estrategia_bajo)))
    cat(sprintf("VALOR TOTAL PROTEGIDO: %s\n", 
                scales::dollar(valor_total_protegido_bajo)))
    cat(sprintf("Efectividad: %.2f%%\n", 
                (valor_total_protegido_bajo / monto_a_cubrir) * 100))
    cat(sprintf("Costo como %% del capital: %.2f%%\n", 
                (costo_total_estrategia_bajo / inversion_inicial) * 100))
    cat(rep("═", 60), "\n\n", sep = "")
  } else {
    cat("\n⚠️  No se pudo implementar la estrategia (sin opciones válidas)\n\n")
    # Definir valores por defecto para evitar errores posteriores
    costo_total_estrategia_bajo <- 0
    valor_total_protegido_bajo <- 0
  }
}
## 
## ► Taiwan Semiconductor (TSM):
##   Acciones: 11,548 | Contratos: 115
##   Strike: $270 | Prima: $18.83
##   Costo Prima: $216,498
##   Costo Intereses (2Y): $4,424.06
##   COSTO TOTAL: $220,922
##   Valor Protegido: $3,105,000
## 
## ► Costco Wholesale Corp. (COST):
##   Acciones: 3,594 | Contratos: 35
##   Strike: $900 | Prima: $36.78
##   Costo Prima: $128,733
##   Costo Intereses (2Y): $2,630.61
##   COSTO TOTAL: $131,363
##   Valor Protegido: $3,150,000
## 
## ► Johnson & Johnson (JNJ):
##   Acciones: 17,865 | Contratos: 178
##   Strike: $180 | Prima: $4.91
##   Costo Prima: $87,471.80
##   Costo Intereses (2Y): $1,787.46
##   COSTO TOTAL: $89,259.25
##   Valor Protegido: $3,204,000
## 
## ════════════════════════════════════════════════════════════
## COSTO TOTAL ESTRATEGIA BAJO: $441,544
## VALOR TOTAL PROTEGIDO: $9,459,000
## Efectividad: 111.28%
## Costo como % del capital: 4.42%
## ════════════════════════════════════════════════════════════

9.3.1.1 Diagrama de Payoff: Protective Put

# ============================================================================
# VISUALIZACIÓN DE PAYOFF - PROTECTIVE PUT
# ============================================================================

# Simular payoff para un activo representativo (primer activo)
activo_ejemplo <- activos[1]
detalles <- estrategia_bajo_detalle[[activo_ejemplo]]

S0 <- precios_actuales[1]
K <- detalles$strike
prima <- detalles$prima_unitaria

# Rango de precios
precios_final <- seq(S0 * 0.5, S0 * 1.5, length.out = 100)

# Payoffs
payoff_accion <- precios_final - S0
payoff_put <- pmax(K - precios_final, 0) - prima
payoff_protegida <- payoff_accion + payoff_put

# DataFrame para graficar
df_payoff <- data.frame(
  Precio = rep(precios_final, 3),
  Payoff = c(payoff_accion, payoff_put, payoff_protegida),
  Componente = rep(c("Solo Acción", "Put Larga", "Posición Protegida"), 
                   each = length(precios_final))
)

# Gráfico
ggplot(df_payoff, aes(x = Precio, y = Payoff, color = Componente, linetype = Componente)) +
  geom_line(size = 1.2) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray40") +
  geom_vline(xintercept = S0, linetype = "dotted", color = "gray60") +
  geom_vline(xintercept = K, linetype = "dotted", color = "#E74C3C") +
  annotate("text", x = S0, y = max(df_payoff$Payoff) * 0.9, 
           label = paste0("S0 = $", round(S0, 2)), 
           hjust = -0.1, size = 4) +
  annotate("text", x = K, y = max(df_payoff$Payoff) * 0.9, 
           label = paste0("Strike = $", round(K, 2)), 
           hjust = 1.1, size = 4, color = "#E74C3C") +
  scale_color_manual(values = c("#2E86AB", "#A23B72", "#27AE60")) +
  scale_linetype_manual(values = c("solid", "dashed", "solid")) +
  labs(
    title = paste("Figura 6. Diagrama de Payoff: Protective Put -", activo_ejemplo),
    subtitle = paste0("Protección de piso mediante Put Americana OTM | Prima: $", 
                     round(prima, 2)),
    x = "Precio del Activo al Vencimiento ($)",
    y = "Ganancia / Pérdida ($)",
    color = "Componente",
    linetype = "Componente"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

Interpretación: La línea verde muestra que las pérdidas están limitadas al nivel del strike, mientras se preserva todo el potencial alcista (menos la prima pagada). Esta es la estrategia óptima cuando se anticipa fuerte caída.


9.3.2 Estrategia 2: Put Spread para Escenario MEDIO

Objetivo: reducir el costo de la cobertura frente a caídas moderadas mediante un spread de débito que no limite el potencial alcista de la posición larga en acciones.

Fundamento: un put spread de débito se construye comprando una put OTM con strike más alto y vendiendo una put con strike más bajo, mismo vencimiento y mismo subyacente, resultando en un costo inicial neto (debit) y con riesgo acotado.

## 
## ► TSM: PutSpread | K_long=$290 K_short=$290 | Débito unit=-$3.26 | Total=-$38,236.56
## 
## ► COST: PutSpread | K_long=$920 K_short=$920 | Débito unit=-$0.38 | Total=-$1,360.48
## 
## ► JNJ: PutSpread | K_long=$190 K_short=$190 | Débito unit=$0 | Total=$0
## 
## ════════════════════════════════════════════════════════════
## COSTO TOTAL ESTRATEGIA MEDIO: -$39,597.05
## ════════════════════════════════════════════════════════════

9.3.2.1 Diagrama de Payoff: Collar

Métrica de payoffs: la pérdida máxima es el débito pagado, la ganancia máxima es el ancho del spread menos el débito, y el punto de equilibrio es \(K_{long}\) − debit.

Interpretación: la posición proporciona protección efectiva entre \(K_{long}\) y \(K_{short}\); si el precio cierra por debajo de \(K_{short}\) se alcanza el beneficio tope del spread, y si cierra por encima de \(K_{long}\) la pérdida queda limitada al débito pagado, manteniendo el upside de la acción sin techo como en el collar.


9.3.3 Estrategia 3: COVERED CALL para Escenario ALTO

Objetivo: Generar ingreso adicional mediante venta de calls OTM.

Fundamento: En el escenario ALTO, con expectativas alcistas moderadas, la venta de calls OTM genera prima inmediata. Si el precio sube moderadamente, se retiene toda la ganancia más la prima. Si sube fuertemente, se limita la ganancia al strike más la prima.

# ============================================================================
# ESTRATEGIA COVERED CALL - ESCENARIO ALTO
# ============================================================================

if(!"ALTO" %in% names(mejores_opciones) || length(mejores_opciones[["ALTO"]]) == 0) {
  cat("⚠️  No hay opciones seleccionadas para escenario ALTO.\n\n")
  ingreso_total_estrategia_alto <- 0
} else {

  estrategia_alto_detalle <- list()

  for(i in 1:length(activos)) {
    activo <- activos[i]

    if(!activo %in% names(mejores_opciones[["ALTO"]])) {
      cat(sprintf("⚠️  No hay opción para %s en ALTO\n", activo))
      next
    }

    call_opcion <- mejores_opciones[["ALTO"]][[activo]]$opcion_principal

    if(is.null(call_opcion) || nrow(call_opcion) == 0) {
      next
    }

    num_acciones_activo <- num_acciones[i]
    num_contratos <- floor(num_acciones_activo / 100)

    ingreso_total <- num_contratos * 100 * call_opcion$`Precio ($)`[1]
    strike_call <- call_opcion$`K ($)`[1]
    ganancia_maxima <- num_contratos * 100 * (strike_call - precios_actuales[i]) + ingreso_total

    estrategia_alto_detalle[[activo]] <- list(
      num_contratos = num_contratos,
      strike_call = strike_call,
      prima_call = call_opcion$`Precio ($)`[1],
      ingreso_total = ingreso_total,
      ganancia_maxima = ganancia_maxima,
      retorno_adicional_pct = (ingreso_total / (num_acciones_activo * precios_actuales[i])) * 100
    )

    cat(sprintf("\n► %s (%s):\n", nombres_activos[i], activo))
    cat(sprintf("  Contratos vendidos: %d\n", num_contratos))
    cat(sprintf("  Strike Call: %s\n", scales::dollar(strike_call)))
    cat(sprintf("  INGRESO TOTAL: %s\n", scales::dollar(ingreso_total)))
    cat(sprintf("  Retorno adicional: %.2f%%\n", 
                estrategia_alto_detalle[[activo]]$retorno_adicional_pct))
  }

  if(length(estrategia_alto_detalle) > 0) {
    ingreso_total_estrategia_alto <- sum(sapply(estrategia_alto_detalle, function(x) x$ingreso_total))

    cat("\n", rep("═", 60), "\n", sep = "")
    cat(sprintf("INGRESO TOTAL ESTRATEGIA ALTO: %s\n", 
                scales::dollar(ingreso_total_estrategia_alto)))
    cat(rep("═", 60), "\n\n", sep = "")
  } else {
    ingreso_total_estrategia_alto <- 0
  }
}
## 
## ► Taiwan Semiconductor (TSM):
##   Contratos vendidos: 115
##   Strike Call: $310
##   INGRESO TOTAL: $267,167
##   Retorno adicional: 8.00%
## 
## ► Costco Wholesale Corp. (COST):
##   Contratos vendidos: 35
##   Strike Call: $950
##   INGRESO TOTAL: $168,586
##   Retorno adicional: 5.08%
## 
## ► Johnson & Johnson (JNJ):
##   Contratos vendidos: 178
##   Strike Call: $200
##   INGRESO TOTAL: $127,599
##   Retorno adicional: 3.82%
## 
## ════════════════════════════════════════════════════════════
## INGRESO TOTAL ESTRATEGIA ALTO: $563,352
## ════════════════════════════════════════════════════════════

9.3.3.1 Diagrama de Payoff: Covered Call

# Payoff covered call
activo_ejemplo <- activos[1]
detalles_cc <- estrategia_alto_detalle[[activo_ejemplo]]

S0 <- precios_actuales[1]
K_call <- detalles_cc$strike_call
prima_call <- detalles_cc$prima_call

precios_final <- seq(S0 * 0.7, S0 * 1.8, length.out = 100)

payoff_accion <- precios_final - S0
payoff_call_corta <- -pmax(precios_final - K_call, 0) + prima_call
payoff_covered_call <- payoff_accion + payoff_call_corta

df_cc <- data.frame(
  Precio = rep(precios_final, 3),
  Payoff = c(payoff_accion, payoff_call_corta, payoff_covered_call),
  Componente = rep(c("Solo Acción", "Call Corta", "Covered Call"), 
                   each = length(precios_final))
)

ggplot(df_cc, aes(x = Precio, y = Payoff, color = Componente, linetype = Componente)) +
  geom_line(size = 1.2) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray40") +
  geom_vline(xintercept = S0, linetype = "dotted", color = "gray60") +
  geom_vline(xintercept = K_call, linetype = "dotted", color = "#E67E22") +
  annotate("text", x = K_call, y = max(df_cc$Payoff) * 0.9, 
           label = paste0("Strike Call = $", round(K_call, 2)), 
           hjust = -0.1, size = 4, color = "#E67E22") +
  annotate("rect", xmin = S0, xmax = K_call, ymin = 0, ymax = Inf,
           alpha = 0.1, fill = "green") +
  scale_color_manual(values = c("#2E86AB", "#E67E22", "#27AE60")) +
  scale_linetype_manual(values = c("solid", "dashed", "solid")) +
  labs(
    title = paste("Figura 8. Diagrama de Payoff: Covered Call -", activo_ejemplo),
    subtitle = paste0("Generación de ingreso mediante venta de Call OTM | Prima: $", 
                     round(prima_call, 2)),
    x = "Precio del Activo al Vencimiento ($)",
    y = "Ganancia / Pérdida ($)",
    color = "Componente",
    linetype = "Componente"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

Interpretación: El área verde muestra la “zona dulce” donde se captura toda la apreciación más la prima. Por encima del strike, la ganancia se limita, pero ya incluye un retorno sustancial. Esta estrategia es óptima en mercados alcistas moderados.


9.4 Comparación Integral de Estrategias

# ============================================================================
# TABLA COMPARATIVA DE LAS TRES ESTRATEGIAS
# ============================================================================

tabla_comparacion_estrategias <- tibble(
  Estrategia = c("Protective Put (BAJO)", "Put Spread (MEDIO)", "Covered Call (ALTO)"),
  "Escenario Óptimo" = c("Mercado Bajista", "Mercado Neutral", "Mercado Alcista"),
  "Costo/Ingreso ($)" = c(
    costo_total_estrategia_bajo,
    costo_total_estrategia_medio,
    -ingreso_total_estrategia_alto  # Negativo porque es ingreso
  ),
  "% del Capital" = c(
    (costo_total_estrategia_bajo / inversion_inicial) * 100,
    (costo_total_estrategia_medio / inversion_inicial) * 100,
    -(ingreso_total_estrategia_alto / inversion_inicial) * 100
  ),
  "Protección Bajista" = c("Máxima", "Moderada", "Ninguna"),
  "Potencial Alcista" = c("Ilimitado", "Limitado", "Limitado al Strike"),
  "Complejidad" = c("Baja", "Media", "Baja"),
  "Renovación" = c("Trimestral", "Trimestral", "Trimestral"),
  "Objetivo Principal" = c(
    "Proteger contra pérdidas",
    "Balance costo-protección",
    "Generar ingreso adicional"
  )
)

tabla_comparacion_estrategias %>%
  gt() %>%
  tab_header(
    title = "Tabla 17. Análisis Comparativo de Estrategias de Cobertura",
    subtitle = "Evaluación integral de tres estrategias diferenciadas por escenario"
  ) %>%
  fmt_currency(columns = "Costo/Ingreso ($)", currency = "USD", decimals = 0) %>%
  fmt_number(columns = "% del Capital", decimals = 2) %>%
  opt_align_table_header(align = "center") %>%
  tab_style(
    style = cell_fill(color = "#FFCDD2"),
    locations = cells_body(rows = 1)
  ) %>%
  tab_style(
    style = cell_fill(color = "#FFF9C4"),
    locations = cells_body(rows = 2)
  ) %>%
  tab_style(
    style = cell_fill(color = "#E8F5E9"),
    locations = cells_body(rows = 3)
  ) %>%
  tab_source_note(
    source_note = md("*Análisis basado en 72 árboles binomiales valuados. Costos incluyen financiamiento con apalancamiento a tasa Rf por 2 años.*")
  )
Tabla 17. Análisis Comparativo de Estrategias de Cobertura
Evaluación integral de tres estrategias diferenciadas por escenario
Estrategia Escenario Óptimo Costo/Ingreso ($) % del Capital Protección Bajista Potencial Alcista Complejidad Renovación Objetivo Principal
Protective Put (BAJO) Mercado Bajista $441,544 4.42 Máxima Ilimitado Baja Trimestral Proteger contra pérdidas
Put Spread (MEDIO) Mercado Neutral −$39,597 −0.40 Moderada Limitado Media Trimestral Balance costo-protección
Covered Call (ALTO) Mercado Alcista −$563,352 −5.63 Ninguna Limitado al Strike Baja Trimestral Generar ingreso adicional
Análisis basado en 72 árboles binomiales valuados. Costos incluyen financiamiento con apalancamiento a tasa Rf por 2 años.

9.4.1 Gráfico Comparativo: Costo vs Protección

# ============================================================================
# VISUALIZACIÓN: COSTO VS PROTECCIÓN
# ============================================================================

df_comp_visual <- data.frame(
  Estrategia = c("Protective Put", "Put Spread", "Covered Call"),
  Costo_Pct = c(
    (costo_total_estrategia_bajo / inversion_inicial) * 100,
    (costo_total_estrategia_medio / inversion_inicial) * 100,
    -(ingreso_total_estrategia_alto / inversion_inicial) * 100
  ),
  Proteccion_Score = c(10, 6, 0),  # Score subjetivo de protección
  Potencial_Alcista = c(10, 6, 6)  # Score de potencial alcista
)

# Gráfico de dispersión
ggplot(df_comp_visual, aes(x = Costo_Pct, y = Proteccion_Score, 
                           color = Estrategia, size = Potencial_Alcista)) +
  geom_point(alpha = 0.7) +
  geom_text(aes(label = Estrategia), vjust = -1, size = 4, show.legend = FALSE) +
  scale_color_manual(values = c("#E74C3C", "#F39C12", "#27AE60")) +
  scale_size_continuous(range = c(8, 15), guide = "none") +
  labs(
    title = "Figura 9. Trade-off entre Costo y Protección de Estrategias",
    subtitle = "Tamaño de puntos representa potencial alcista",
    x = "Costo Neto (% del Capital)",
    y = "Score de Protección (0-10)",
    color = "Estrategia"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "bottom"
  ) +
  xlim(-2, max(df_comp_visual$Costo_Pct) + 1) +
  ylim(-1, 11)


9.5 Análisis de Sensibilidad

9.5.1 Sensibilidad a Cambios en Volatilidad

# ============================================================================
# ANÁLISIS DE SENSIBILIDAD: IMPACTO DE CAMBIOS EN VOLATILIDAD
# ============================================================================

# Evaluar cómo cambian los precios de opciones con diferentes niveles de vol

# Rango de volatilidades a evaluar
vol_range <- seq(0.15, 0.50, by = 0.05)

# Función para recalcular precios de opciones
recalcular_precio_opcion <- function(S0, K, r, sigma, T, tipo, ejercicio) {
  # Usar modelo binomial
  arbol <- calcular_arbol_8_pasos(
    S0 = S0,
    K = K,
    r = r,
    sigma = sigma,
    tipo_opcion = tipo,
    tipo_ejercicio = ejercicio,
    tipo_volatilidad = "Ajustada"
  )
  return(arbol$precio_opcion)
}

# Analizar sensibilidad para un activo representativo
activo_analisis <- activos[1]
S0_analisis <- precios_actuales[1]

# Para Protective Put
K_put_analisis <- estrategia_bajo_detalle[[activo_analisis]]$strike

precios_put_vol <- sapply(vol_range, function(vol) {
  recalcular_precio_opcion(
    S0 = S0_analisis,
    K = K_put_analisis,
    r = tasa_libre_riesgo,
    sigma = vol,
    T = 2,
    tipo = "put",
    ejercicio = "americana"
  )
})

# DataFrame
df_sens_vol <- data.frame(
  Volatilidad = vol_range * 100,
  "Precio Put ($)" = precios_put_vol,
  "Costo por Contrato ($)" = precios_put_vol * 100,
  check.names = FALSE
)

# Tabla
df_sens_vol %>%
  gt() %>%
  tab_header(
    title = paste("Tabla 18. Sensibilidad del Precio de Put a Volatilidad -", activo_analisis),
    subtitle = paste0("S0 = $", round(S0_analisis, 2), " | K = $", round(K_put_analisis, 2))
  ) %>%
  fmt_number(columns = "Volatilidad", decimals = 1, pattern = "{x}%") %>%
  fmt_currency(columns = c("Precio Put ($)", "Costo por Contrato ($)"), 
               currency = "USD", decimals = 4) %>%
  opt_align_table_header(align = "center") %>%
  data_color(
    columns = "Precio Put ($)",
    colors = scales::col_numeric(
      palette = c("#E8F5E9", "#FFF9C4", "#FFCDD2"),
      domain = NULL
    )
  )
Tabla 18. Sensibilidad del Precio de Put a Volatilidad - TSM
S0 = $289.24 | K = $270
Volatilidad Precio Put ($) Costo por Contrato ($)
15.0% $13.9663 $1,396.6326
20.0% $21.2959 $2,129.5929
25.0% $28.6601 $2,866.0099
30.0% $36.0128 $3,601.2831
35.0% $43.3293 $4,332.9292
40.0% $50.5928 $5,059.2837
45.0% $57.7906 $5,779.0642
50.0% $64.9120 $6,491.1964
# Gráfico
ggplot(df_sens_vol, aes(x = Volatilidad, y = `Precio Put ($)`)) +
  geom_line(color = "#E74C3C", size = 1.2) +
  geom_point(color = "#C0392B", size = 3) +
  geom_vline(xintercept = vol_impl[1] * 100, 
             linetype = "dashed", color = "blue", alpha = 0.5) +
  annotate("text", x = vol_impl[1] * 100, y = max(precios_put_vol) * 0.9,
           label = paste0("Vol Implícita Actual\n", round(vol_impl[1] * 100, 1), "%"),
           hjust = -0.1, size = 4, color = "blue") +
  labs(
    title = "Figura 10. Sensibilidad del Precio de Put a la Volatilidad",
    subtitle = paste0("Activo: ", activo_analisis, " | Protective Put Estrategia"),
    x = "Volatilidad (%)",
    y = "Precio de la Put ($)"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5)
  )

Análisis Vega: El gráfico muestra que el precio de la put es altamente sensible a cambios en la volatilidad implícita. Un incremento de 5 puntos porcentuales en volatilidad puede aumentar el costo de cobertura significativamente. Esto justifica monitorear constantemente la superficie de volatilidad del mercado.


9.5.2 Sensibilidad a Cambios en Tasas de Interés

# Evaluar impacto de cambios en tasa libre de riesgo

tasa_range <- seq(0.02, 0.06, by = 0.01)

precios_put_tasa <- sapply(tasa_range, function(tasa) {
  recalcular_precio_opcion(
    S0 = S0_analisis,
    K = K_put_analisis,
    r = tasa,
    sigma = vol_impl[1],
    T = 2,
    tipo = "put",
    ejercicio = "americana"
  )
})

df_sens_tasa <- data.frame(
  "Tasa (%)" = tasa_range * 100,
  "Precio Put ($)" = precios_put_tasa,
  check.names = FALSE
)

# Gráfico
ggplot(df_sens_tasa, aes(x = `Tasa (%)`, y = `Precio Put ($)`)) +
  geom_line(color = "#3498DB", size = 1.2) +
  geom_point(color = "#2874A6", size = 3) +
  geom_vline(xintercept = tasa_libre_riesgo * 100, 
             linetype = "dashed", color = "red", alpha = 0.5) +
  annotate("text", x = tasa_libre_riesgo * 100, y = max(precios_put_tasa) * 0.9,
           label = paste0("Tasa Actual\n", round(tasa_libre_riesgo * 100, 2), "%"),
           hjust = -0.1, size = 4, color = "red") +
  labs(
    title = "Figura 11. Sensibilidad del Precio de Put a la Tasa de Interés",
    subtitle = "Impacto de Rho en estrategia Protective Put",
    x = "Tasa Libre de Riesgo (%)",
    y = "Precio de la Put ($)"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5)
  )

Análisis Rho: Las puts tienen Rho negativo, su precio disminuye cuando aumentan las tasas. Sin embargo, el impacto es moderado comparado con Vega. Un aumento de 100bps en tasas reduce el precio de la put en aproximadamente 5-7%.


9.6 Simulación de Resultados por Estrategia

9.6.1 Performance Histórica Simulada

# ============================================================================
# SIMULACIÓN DE PERFORMANCE DE ESTRATEGIAS
# ============================================================================

# Usar las simulaciones Monte Carlo existentes para evaluar performance

# Función para calcular retorno de estrategia en cada trayectoria
calcular_retorno_estrategia <- function(trayectoria, estrategia_tipo, activo_idx) {

  S0 <- precios_actuales[activo_idx]
  ST <- trayectoria[length(trayectoria)]

  # Retorno de solo acción
  retorno_accion <- (ST - S0) / S0

  if(estrategia_tipo == "protective_put") {
    # Protective Put
    K <- estrategia_bajo_detalle[[activos[activo_idx]]]$strike
    prima <- estrategia_bajo_detalle[[activos[activo_idx]]]$prima_unitaria

    payoff_put <- max(K - ST, 0)
    retorno_total <- ((ST - S0) + payoff_put - prima) / S0

  } else if(estrategia_tipo == "Put Spread") {
    # Collar
    K_put <- estrategia_medio_detalle[[activos[activo_idx]]]$strike_put
    K_call <- estrategia_medio_detalle[[activos[activo_idx]]]$strike_call
    costo_neto <- estrategia_medio_detalle[[activos[activo_idx]]]$prima_put - 
                  estrategia_medio_detalle[[activos[activo_idx]]]$prima_call

    payoff_put <- max(K_put - ST, 0)
    payoff_call <- -max(ST - K_call, 0)

    retorno_total <- ((ST - S0) + payoff_put + payoff_call - costo_neto) / S0

  } else {  # covered_call
    # Covered Call
    K_call <- estrategia_alto_detalle[[activos[activo_idx]]]$strike_call
    prima <- estrategia_alto_detalle[[activos[activo_idx]]]$prima_call

    payoff_call <- -max(ST - K_call, 0)
    retorno_total <- ((ST - S0) + payoff_call + prima) / S0
  }

  return(list(
    sin_cobertura = retorno_accion,
    con_estrategia = retorno_total
  ))
}

# Simular para primer activo como representativo
activo_sim <- 1
n_sims_eval <- ncol(sim_TSM$simulaciones)

resultados_sim <- data.frame(
  sim_id = 1:n_sims_eval,
  sin_cobertura = numeric(n_sims_eval),
  protective_put = numeric(n_sims_eval),
  collar = numeric(n_sims_eval),
  covered_call = numeric(n_sims_eval)
)

for(sim in 1:n_sims_eval) {
  trayectoria <- sim_TSM$simulaciones[, sim]

  resultados_sim$sin_cobertura[sim] <- (trayectoria[length(trayectoria)] - 
                                        precios_actuales[activo_sim]) / 
                                        precios_actuales[activo_sim]

  resultados_sim$protective_put[sim] <- calcular_retorno_estrategia(
    trayectoria, "protective_put", activo_sim)$con_estrategia

  resultados_sim$PutSpread[sim] <- calcular_retorno_estrategia(
    trayectoria, "PutSpread", activo_sim)$con_estrategia

  resultados_sim$covered_call[sim] <- calcular_retorno_estrategia(
    trayectoria, "covered_call", activo_sim)$con_estrategia
}

# Estadísticas
estadisticas_estrategias <- data.frame(
  Estrategia = c("Sin Cobertura", "Protective Put", "PutSpread", "Covered Call"),
  "Retorno Promedio (%)" = c(
    mean(resultados_sim$sin_cobertura) * 100,
    mean(resultados_sim$protective_put) * 100,
    mean(resultados_sim$PutSpread) * 100,
    mean(resultados_sim$covered_call) * 100
  ),
  "Desv. Estándar (%)" = c(
    sd(resultados_sim$sin_cobertura) * 100,
    sd(resultados_sim$protective_put) * 100,
    sd(resultados_sim$PutSpread) * 100,
    sd(resultados_sim$covered_call) * 100
  ),
  "Sharpe Ratio" = c(
    mean(resultados_sim$sin_cobertura) / sd(resultados_sim$sin_cobertura),
    mean(resultados_sim$protective_put) / sd(resultados_sim$protective_put),
    mean(resultados_sim$PutSpread) / sd(resultados_sim$PutSpread),
    mean(resultados_sim$covered_call) / sd(resultados_sim$covered_call)
  ),
  "Retorno Mínimo (%)" = c(
    min(resultados_sim$sin_cobertura) * 100,
    min(resultados_sim$protective_put) * 100,
    min(resultados_sim$PutSpread) * 100,
    min(resultados_sim$covered_call) * 100
  ),
  "Retorno Máximo (%)" = c(
    max(resultados_sim$sin_cobertura) * 100,
    max(resultados_sim$protective_put) * 100,
    max(resultados_sim$PutSpread) * 100,
    max(resultados_sim$covered_call) * 100
  ),
  check.names = FALSE
)

# Tabla
estadisticas_estrategias %>%
  gt() %>%
  tab_header(
    title = paste("Tabla 19. Estadísticas de Performance Simulada -", activos[activo_sim]),
    subtitle = paste0("Basado en ", n_sims_eval, " simulaciones Monte Carlo de 10 años")
  ) %>%
  fmt_number(columns = c("Retorno Promedio (%)", "Desv. Estándar (%)", 
                         "Retorno Mínimo (%)", "Retorno Máximo (%)"), 
             decimals = 2) %>%
  fmt_number(columns = "Sharpe Ratio", decimals = 3) %>%
  opt_align_table_header(align = "center") %>%
  tab_style(
    style = cell_fill(color = "#E8F5E9"),
    locations = cells_body(rows = which.max(estadisticas_estrategias$`Sharpe Ratio`))
  ) %>%
  tab_source_note(
    source_note = "Estrategia con mejor Sharpe Ratio resaltada en verde."
  )
Tabla 19. Estadísticas de Performance Simulada - TSM
Basado en 500 simulaciones Monte Carlo de 10 años
Estrategia Retorno Promedio (%) Desv. Estándar (%) Sharpe Ratio Retorno Mínimo (%) Retorno Máximo (%)
Sin Cobertura 9.34 16.45 0.568 −29.18 61.38
Protective Put 3.91 14.93 0.262 −13.16 54.87
PutSpread 9.81 7.86 1.249 −21.15 15.21
Covered Call 9.81 7.86 1.249 −21.15 15.21
Estrategia con mejor Sharpe Ratio resaltada en verde.

La tabla de estadísticas de performance presenta una comparación exhaustiva entre distintas estrategias de manejo del riesgo en TSM durante 500 simulaciones Monte Carlo a 10 años. Observamos que la estrategia de Protective Put reduce considerablemente el riesgo (menor desviación estándar y retorno mínimo menos negativo) pero, al mismo tiempo, sacrifica retorno promedio y ratio de Sharpe respecto a las alternativas de inversión más agresivas, como el PutSpread y el Covered Call. Las estrategias de PutSpread y Covered Call logran el mayor Sharpe Ratio (1.249), lo que indica una óptima relación entre retorno y riesgo asumido, superando incluso el caso sin cobertura y mostrando menor volatilidad. Este resultado es coherente con la literatura, donde las estrategias de spreads y calls cubiertos permiten una mejor captura de primas y control de riesgo en mercados alcistas o neutrales, mientras el uso de puts protectivos es preferible para escenarios de alta incertidumbre y caída esperada (Brigham & Ehrhardt, 2017; Hull, 2018).

9.6.2 Distribuciones de Retornos

# ============================================================================
# VISUALIZACIÓN: DISTRIBUCIONES DE RETORNOS
# ============================================================================

# Preparar datos para histogramas
df_retornos_long <- data.frame(
  Retorno = c(resultados_sim$sin_cobertura,
              resultados_sim$protective_put,
              resultados_sim$PutSpread,
              resultados_sim$covered_call),
  Estrategia = rep(c("Sin Cobertura", "Protective Put", "PutSpread", "Covered Call"),
                   each = n_sims_eval)
) %>%
  mutate(Retorno_Pct = Retorno * 100)

# Histogramas facetados
ggplot(df_retornos_long, aes(x = Retorno_Pct, fill = Estrategia)) +
  geom_histogram(bins = 40, alpha = 0.7, color = "white") +
  geom_vline(data = df_retornos_long %>% 
               group_by(Estrategia) %>% 
               summarise(media = mean(Retorno_Pct)),
             aes(xintercept = media, color = Estrategia), 
             linetype = "dashed", size = 1) +
  facet_wrap(~Estrategia, ncol = 2, scales = "free_y") +
  scale_fill_manual(values = c("#7F8C8D", "#E74C3C", "#F39C12", "#27AE60")) +
  scale_color_manual(values = c("#34495E", "#C0392B", "#E67E22", "#229954")) +
  labs(
    title = "Figura 12. Distribuciones de Retornos por Estrategia",
    subtitle = paste0("Simulaciones Monte Carlo (N=", n_sims_eval, ") - Activo: ", 
                     activos[activo_sim]),
    x = "Retorno (%)",
    y = "Frecuencia"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "none",
    strip.text = element_text(face = "bold", size = 10)
  )

Interpretación Visual:

  • Sin Cobertura: Distribución más amplia, mayor dispersión de resultados
  • Protective Put: Cola izquierda truncada (protección bajista), preserva cola derecha
  • PutSpread: Distribución concentrada, colas truncadas en ambos extremos
  • Covered Call: Cola derecha truncada, sacrifica extremos alcistas

10 Análisis del Impacto de la Cobertura

10.1 Efectividad de la Protección

La efectividad de una estrategia de cobertura se mide por su capacidad de reducir pérdidas potenciales manteniendo un costo razonable. Evaluamos múltiples dimensiones del impacto.

La gráfica muestra la evolución temporal del portafolio bajo escenario bajo, contrastando su valor con el piso de protección ofrecido por la estrategia de Protective Put renovada trimestralmente. Se observa que la serie del portafolio nunca cruza el piso azul, manteniéndose consistentemente por encima del nivel de cobertura establecido. Esto evidencia que el portafolio fue suficientemente robusto durante la simulación y que la protección no fue activada, aunque permaneció disponible en todo momento. Tal como menciona la teoría financiera moderna, la cobertura se comporta como una “póliza de seguro”: si bien en este escenario no generó pagos directos de protección, aportó certidumbre y transferibilidad de riesgo, lo que se traduce en tranquilidad para el gestor frente a posibles escenarios adversos (Hull, 2018).

Esta gráfica compara el valor final del portafolio con y sin cobertura al cierre de cada escenario (bajo, medio y alto). Se aprecia que, en los escenarios medio y alto, el portafolio protegido logra superar al portafolio sin cobertura, lo cual sugiere que, especialmente en mercados favorables o con baja volatilidad, el impacto del costo de las primas no penaliza el desempeño global. En el escenario bajo, la diferencia de valor final es menor para la cobertura; esto refleja el costo de protección en condiciones donde la simulación no generó grandes caídas. El análisis confirma que la efectividad de las estrategias de cobertura está directamente relacionada con la magnitud del riesgo enfrentado y la volatilidad del mercado, siendo crucial ajustar la estrategia al contexto financiero y a la probabilidad de siniestros (Brigham & Ehrhardt, 2017; Hull, 2018).

10.2 Conclusiones y Recomendaciones Estratégicas

10.2.1 Recomendaciones por Tipo de Inversor

# ============================================================================
# MATRIZ DE RECOMENDACIONES POR PERFIL DE RIESGO
# ============================================================================

matriz_recomendaciones <- tibble(
  "Perfil de Inversor" = c(
    "Conservador",
    "Moderado",
    "Agresivo",
    "Institucional (Fiduciario)"
  ),
  "Estrategia Recomendada" = c(
    "Protective Put (Escenario BAJO)",
    "PutSpread (Escenario MEDIO)",
    "Sin cobertura o Covered Call ligero",
    "PutSpread o Protective Put según mandato"
  ),
  "Justificación" = c(
    "Prioridad máxima en protección de capital. Costo justificado por preservación.",
    "Balance óptimo entre protección y costo. Financiamiento reduce impacto.",
    "Busca maximizar retornos. Covered Call solo para generar ingreso incremental.",
    "Cumplimiento regulatorio. Documentar estrategia de mitigación de riesgo."
  ),
  "Costo Esperado (% Capital)" = c(
    paste0(round((costo_total_estrategia_bajo / inversion_inicial) * 100, 2), "%"),
    paste0(abs(round((costo_total_estrategia_medio / inversion_inicial) * 100, 2)), "%"),
    "0% - Genera ingreso adicional",
    "Variable según requerimientos"
  ),
  "Renovación" = c(
    "Trimestral, monitoreo continuo",
    "Trimestral, ajustar strikes según mercado",
    "Oportunista",
    "Trimestral, reportar a comité"
  )
)

matriz_recomendaciones %>%
  gt() %>%
  tab_header(
    title = "Tabla 20. Matriz de Recomendaciones Estratégicas por Perfil de Riesgo",
    subtitle = "Guía para selección de estrategia de cobertura"
  ) %>%
  cols_width(
    "Perfil de Inversor" ~ px(150),
    "Estrategia Recomendada" ~ px(200),
    "Justificación" ~ px(300),
    "Costo Esperado (% Capital)" ~ px(150),
    "Renovación" ~ px(200)
  ) %>%
  opt_align_table_header(align = "center") %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_body(columns = "Perfil de Inversor")
  )
Tabla 20. Matriz de Recomendaciones Estratégicas por Perfil de Riesgo
Guía para selección de estrategia de cobertura
Perfil de Inversor Estrategia Recomendada Justificación Costo Esperado (% Capital) Renovación
Conservador Protective Put (Escenario BAJO) Prioridad máxima en protección de capital. Costo justificado por preservación. 4.42% Trimestral, monitoreo continuo
Moderado PutSpread (Escenario MEDIO) Balance óptimo entre protección y costo. Financiamiento reduce impacto. 0.4% Trimestral, ajustar strikes según mercado
Agresivo Sin cobertura o Covered Call ligero Busca maximizar retornos. Covered Call solo para generar ingreso incremental. 0% - Genera ingreso adicional Oportunista
Institucional (Fiduciario) PutSpread o Protective Put según mandato Cumplimiento regulatorio. Documentar estrategia de mitigación de riesgo. Variable según requerimientos Trimestral, reportar a comité

10.2.2 Consideraciones de Implementación

1. Timing de Ejecución: - Ejecutar al inicio de cada trimestre - Monitorear superficie de volatilidad implícita - Aprovechar dislocaciones temporales en precios de opciones

2. Selección de Strikes: - Protective Put: 5-10% OTM para balance costo-protección - PutSpread: Put 5% OTM, Call 10-15% OTM - Covered Call: 10-20% OTM para preservar upside razonable

3. Gestión de Colateral: - Covered Calls no requieren colateral adicional (posición cubierta) - Protective Puts: prima se financia con deuda - PutSpread: verificar margin requirements del bróker

4. Tratamiento Fiscal: - Consultar con asesor fiscal - Opciones pueden calificar como hedges para propósitos contables - Documentar intención económica de cobertura

5. Monitoreo y Ajustes: - Revisar semanalmente: Delta del portafolio, cambios en vol implícita - Triggers para ajuste anticipado: - Cambio >5% en volatilidad implícita - Movimiento >10% en precio subyacente - Eventos corporativos (earnings, splits)


FIN DEL ANÁLISIS INTEGRAL


Análisis Cuantitativo de Derivados Financieros
Cobertura de Portafolio con Árboles Binomiales

Documento generado el 12 de noviembre de 2025


Este análisis se realizó con fines académicos y de investigación.
Las estrategias presentadas deben ser evaluadas por profesionales
antes de su implementación en mercados reales.


11 Referencias


Brigham, E. F., & Ehrhardt, M. C. (2017). Financial management: Theory & practice (15th ed.). Cengage Learning.
Dumas, B., Fleming, J., & Whaley, R. (1998). Implied volatility functions: Empirical tests. The Journal of Finance, 53(6), 2059–2106. https://doi.org/10.1111/0022-1082.00085
Finance, Y. (2025). Equity option chains: TSM, COST, JNJ january 2028. https://finance.yahoo.com/
Hull, J. C. (2018). Options, futures, and other derivatives (10th ed.). Pearson.
Markowitz, H. (1952). Portfolio selection. The Journal of Finance, 7(1), 77–91. https://doi.org/10.2307/2975974
Sharpe, W. F. (1994). The sharpe ratio. The Journal of Portfolio Management, 21(1), 49–58.