library(quantmod)
library(PerformanceAnalytics)
library(tidyverse)
library(ggplot2)
library(dplyr)
library(reshape2)
library(kableExtra)
library(TTR)
library(MASS)
Seleccionamos las 3 acciones Delek Logistics Partners LP (DKL), Kimbell Royalty (KRP) y FinVolution Group (FINV) con las cuales se llevará a cabo un análisis de cobertura europea y americana, utilizando datos recopilados desde el 01/06/2022. Se considerará una opción de 3 acciones, que liquide de forma trimestral hasta la fecha de inversión máxima de la opción (o máximo dos años en el futuro), donde el precio strike se determinará conforme los criterios de liquidez (bid-ask), volatilidad implícita y open interest. Se realizará un “rolling” al finalizar cada contrato, ajustando el precio de prima cada trimestre. La tasa libre de riesgo se establecerá a un trimestre, basada en los bonos del Tesoro (Tbond).
tickers <- c("DKL", "KRP", "FINV") # Cambia estos tickers según tus preferencias
start_date <- "2022-06-01"
end_date <- Sys.Date()
prices_list <- list() # Lista para almacenar precios ajustados
returns_list <- list() # Lista para almacenar retornos logarítmicos
Descargar los datos históricos de precios y calcular los retornos
for(ticker in tickers) {
getSymbols(ticker, src = 'yahoo', from = start_date, to = end_date, auto.assign = TRUE)
price <- Ad(get(ticker)) # Extraer precios ajustados
prices_list[[ticker]] <- price
ret <- diff(log(price)) # Calcular los rendimientos logarítmicos diarios
returns_list[[ticker]] <- ret
}
# Parámetros de simulación
n_simulations <- 5000
n_years <- 2
n_quarters <- n_years * 8 # 8 trimestres
dt <- 90/252 # Paso temporal para un trimestre (90 días)
simulated_prices_list <- list() # Lista para almacenar las simulaciones por acción
# Dividir la ventana gráfica en 1 fila y 1 columna (un gráfico por vez)
par(mfrow = c(1, 1))
# Simular precios para cada acción
for(ticker in tickers) {
ret <- returns_list[[ticker]]
mean_ret_daily <- mean(ret, na.rm = TRUE)
vol_daily <- sd(ret, na.rm = TRUE)
initial_price <- as.numeric(last(prices_list[[ticker]])) # Último precio observado
sim_prices <- matrix(0, nrow = n_simulations, ncol = n_quarters)
# Simulación de precios
for(i in 1:n_simulations) {
price_sim <- initial_price
for(q in 1:n_quarters) {
drift <- (mean_ret_daily - 0.5 * vol_daily^2) * dt
shock <- vol_daily * sqrt(dt) * rnorm(1)
price_sim <- price_sim * exp(drift + shock)
sim_prices[i, q] <- price_sim
}
}
simulated_prices_list[[ticker]] <- sim_prices # Almacenar las simulaciones
# Graficar las simulaciones
num_to_plot <- min(5000, n_simulations) # Graficar hasta 5000 simulaciones
matplot(t(sim_prices[1:num_to_plot,]), type = "l", lty = 1, col = 1:num_to_plot,
xlab = "Trimestre", ylab = "Precio Simulado",
main = paste("Simulaciones de precios para", ticker))
}
expected_prices_list <- list()
for(ticker in tickers) {
sim_prices <- simulated_prices_list[[ticker]]
expected_prices <- colMeans(sim_prices) # Promedio de los precios simulados
last_8_prices <- tail(expected_prices, 8) # Obtener los últimos 8 precios esperados
expected_prices_list[[ticker]] <- last_8_prices
cat("\nÚltimos 8 precios esperados para", ticker, ":\n")
print(last_8_prices)
}
##
## Últimos 8 precios esperados para DKL :
## [1] 39.75871 39.74732 39.75070 39.74911 39.74800 39.74501 39.75376 39.76982
##
## Últimos 8 precios esperados para KRP :
## [1] 12.35889 12.35720 12.35250 12.35253 12.35573 12.35552 12.35661 12.35772
##
## Últimos 8 precios esperados para FINV :
## [1] 8.779355 8.784579 8.786385 8.790512 8.795567 8.797005 8.793920 8.799888
DKL
• Muestra una clara estabilización en torno a los 39.7 USD, con una diferencia mínima entre el precio más alto y el más bajo (~0.024 USD).
• Esta estabilidad puede reflejar que el activo se encuentra en una zona de equilibrio entre oferta y demanda en el modelo simulado.
• Bajo potencial alcista, pero también riesgo limitado en el corto plazo.
Activo con bajo momentum esperado. Ideal para estrategias conservadoras o uso como activo defensivo. Requiere otros catalizadores externos para ganar valor (dividendos, fundamentales, etc.).
KRP
• También presenta una tendencia estable en el rango 12.33 - 12.35, con una ligera tendencia a la baja.
• Esto podría indicar un leve deterioro en la valoración proyectada, aunque muy contenido.
• Puede ser señal de agotamiento en impulso alcista,pero sin riesgo sistémico.
Ligeramente bajista, pero con estabilidad. Puede mantenerse con estrategia de cobertura parcial, o rotarse si se busca mayor crecimiento de capital.
FINV
Interpretación:
• Contrario a DKL y KRP, muestra una tendencia claramente alcista, desde 8.77 hasta casi 8.80 USD.
• Aumentos constantes y suaves: señal de momento positivo sostenido en la simulación.
• Indica una percepción de mercado más favorable o mayor sensibilidad positiva a factores económicos proyectados.
Buen candidato para mantener o aumentar exposición. Posible fuente de retorno creciente en próximos trimestres, especialmente útil como activo ofensivo en portafolios diversificados.
for(ticker in tickers) {
sim_prices <- simulated_prices_list[[ticker]]
S_min <- apply(sim_prices, 1, min) # Precio mínimo por simulación
min_price <- min(S_min) # Mínimo global
cat("\n", ticker, "Precio mínimo en las simulaciones de la acción: ", min_price, "\n")
}
##
## DKL Precio mínimo en las simulaciones de la acción: 33.88911
##
## KRP Precio mínimo en las simulaciones de la acción: 10.69876
##
## FINV Precio mínimo en las simulaciones de la acción: 7.105387
DKL
• Tendencia de precio estable: precios esperados alrededor de 39.7 USD por 8 trimestres consecutivos.
• Volatilidad trimestral: ≈ 0.20 → moderadamente alta para un activo lateral.
• Sharpe ratio promedio: ≈ -0.037 → retorno ajustado por riesgo insuficiente.
DKL parece estar en un periodo de madurez o estancamiento de precio, donde la volatilidad aún es significativa pero no se traduce en valorización esperada. Esto representa riesgo sin compensación clara de retorno, una situación subóptima.
Evitar sobreexposición. Buena candidata para estrategia de ingresos (dividendos) si se confirma flujo estable. Recomendable usar puts de protección (OTM) en portafolios conservadores o rotar hacia activos con mejor retorno ajustado por riesgo.
KRP
• Tendencia ligeramente descendente (de 12.346 a 12.332).
• Volatilidad trimestral: ≈ 0.16 → menor que DKL, pero aún moderada.
• Sharpe ratio promedio: ≈ -0.036 → riesgo sin beneficio claro.
Proyecta una leve depreciación del valor con alta incertidumbre. El comportamiento es errático y sin dirección clara, lo cual impide predecir un crecimiento estructurado en el corto plazo.
Solo mantener si hay factores externos (dividendos, fundamentales) que lo justifiquen. Ideal aplicar estrategias defensivas como puts ITM o considerar swap por activos con mejor eficiencia.
FINV
• Tendencia alcista constante (de 8.777 a 8.798).
• Volatilidad trimestral: ≈ 0.24 → mayor que DKL y KRP.
• Sharpe ratio promedio: ≈ 0.43 → positivo y sostenido.
FINV muestra fuerza relativa y una proyección consistente de valorización. A pesar de la alta volatilidad, el ratio Sharpe favorable indica que el riesgo sí está siendo compensado por rendimiento.
Excelente opción para mantener o aumentar exposición. Puede ser activo base para estrategias ofensivas, o bien para financiar coberturas de otros activos menos eficientes. Se recomienda evaluar calls de largo plazo o derivados que amplifiquen retornos.
CONCLUSIÓN
• FINV es el activo con mayor potencial de generación de ganancias, respaldado por estructura alcista, buen Sharpe y aceptable volatilidad.
• DKL y KRP presentan riesgo no compensado por retorno, por lo que deben gestionarse mediante estrategias de cobertura estructurada, o rotarse si se busca crecimiento.
getSymbols("TB3MS", src = "FRED")
## [1] "TB3MS"
risk_free_rate <- as.numeric(last(TB3MS)) / 100 # Tasa libre de riesgo
cat("Tasa libre de riesgo:", risk_free_rate, "\n")
## Tasa libre de riesgo: 0.042
dividendos_recientes <- list()
for (ticker in tickers) {
dividendos <- getDividends(ticker, from = start_date, to = end_date)
if (length(dividendos) > 0) {
ultimo_dividendo <- tail(dividendos, 1)
dividendos_recientes[[ticker]] <- ultimo_dividendo
} else {
dividendos_recientes[[ticker]] <- NA # Si no hay dividendos
}
}
# Mostrar el dividendo más reciente
for (ticker in tickers) {
cat("\nÚltimo dividendo de", ticker, ":\n")
print(dividendos_recientes[[ticker]])
}
##
## Último dividendo de DKL :
## DKL.div
## 2025-02-04 1.105
##
## Último dividendo de KRP :
## KRP.div
## 2025-03-18 0.4
##
## Último dividendo de FINV :
## FINV.div
## 2024-04-15 0.237
var_per_quarter_1 <- list()
var_per_quarter_5 <- list()
for(ticker in tickers) {
ret <- returns_list[[ticker]]
num_quarters <- floor(length(ret) / 90) # Número de trimestres completos
quarterly_var_1 <- numeric(num_quarters)
quarterly_var_5 <- numeric(num_quarters)
for(i in 1:num_quarters) {
quarterly_ret <- ret[((i - 1) * 90 + 1):(i * 90)]
var_1 <- quantile(quarterly_ret, 0.01, na.rm = TRUE)
var_5 <- quantile(quarterly_ret, 0.05, na.rm = TRUE)
quarterly_var_1[i] <- var_1
quarterly_var_5[i] <- var_5
}
var_per_quarter_1[[ticker]] <- quarterly_var_1
var_per_quarter_5[[ticker]] <- quarterly_var_5
}
# Mostrar VaR al 1% y 5% por trimestre
for(ticker in tickers) {
cat("\nVaR al 1% por trimestre para", ticker, ":\n")
print(var_per_quarter_1[[ticker]])
cat("\nVaR al 5% por trimestre para", ticker, ":\n")
print(var_per_quarter_5[[ticker]])
}
##
## VaR al 1% por trimestre para DKL :
## [1] -0.06619428 -0.03825047 -0.03835281 -0.06287406 -0.08038186 -0.01714089
## [7] -0.03741766
##
## VaR al 5% por trimestre para DKL :
## [1] -0.05032793 -0.03028710 -0.03341852 -0.04784792 -0.02976558 -0.01427280
## [7] -0.01624259
##
## VaR al 1% por trimestre para KRP :
## [1] -0.05948552 -0.05489649 -0.03653016 -0.03813148 -0.02335469 -0.02837009
## [7] -0.02773468
##
## VaR al 5% por trimestre para KRP :
## [1] -0.04715284 -0.03859656 -0.02950043 -0.01780650 -0.01708682 -0.01887579
## [7] -0.02159327
##
## VaR al 1% por trimestre para FINV :
## [1] -0.07672202 -0.06445660 -0.07133194 -0.04444302 -0.05728157 -0.03832083
## [7] -0.05105264
##
## VaR al 5% por trimestre para FINV :
## [1] -0.05302158 -0.03920045 -0.03375320 -0.02918700 -0.03169952 -0.02353392
## [7] -0.04345339
DKL – Riesgo elevado e inestable
• VaR al 1% muy volátil, con trimestres de riesgo extremo (> -6%) como el Q1 y Q5.
• El riesgo no es progresivamente decreciente, lo cual sugiere falta de control sobre eventos adversos.
• A pesar de algunos trimestres más tranquilos, el perfil general de riesgo es agresivo y disperso.
Estrategia:
• Activo inestable ante shocks de mercado.
• Requiere cobertura activa con puts OTM y/o seguros de portafolio, especialmente en trimestres con volatilidad proyectada alta.
KRP – Riesgo recurrente pero menos severo
• Riesgo más estable, sin extremos tan amplios como DKL.
• Presenta riesgo de cola elevado pero predecible, útil para modelos de cobertura estructurada.
• La reducción gradual del VaR al 5% en los últimos trimestres sugiere un ligero descenso en la severidad de pérdidas.
Estrategia:
• Activo con riesgo controlable pero frecuente.
• Ideal para mantener con estrategia de puts ITM para protección frente a pérdidas recurrentes.
• Apto para inversionistas que buscan activos volátiles con sistemas de cobertura integrados.
FINV – Alto riesgo extremo con retorno compensatorio
• El más riesgoso en eventos extremos (VaR 1%) del portafolio.
• Sin embargo, su Índice de Sharpe promedio es superior (≈ 0.43), lo que justifica parcialmente su mayor exposición al riesgo.
• Algunos trimestres tienen VaR alto, pero bien compensado por retornos esperados y consistencia positiva.
Estrategia:
• Activo de perfil agresivo pero rentable.
• Ideal para inversionistas tolerantes al riesgo, preferentemente con seguimiento dinámico (delta hedging o trailing stop) en entornos volátiles.
volatilidad_list <- list()
for(ticker in tickers) {
ret <- returns_list[[ticker]]
volatilidad_diaria <- sd(ret, na.rm = TRUE)
volatilidad_trimestral <- volatilidad_diaria * sqrt(90) # 90 días por trimestre
volatilidad_list[[ticker]] <- volatilidad_trimestral
}
# Imprimir la volatilidad trimestral
print(volatilidad_list)
## $DKL
## [1] 0.2019465
##
## $KRP
## [1] 0.1646831
##
## $FINV
## [1] 0.2408963
DKL
• Se ubica en una zona de riesgo moderado, cercana al umbral alto.
• Implica que los retornos fluctúan con cierta intensidad, pero no de forma extrema.
• Requiere seguimiento de tendencias macroeconómicas o eventos sectoriales que puedan ampliar ese riesgo.
Acción con riesgo gestionable pero no despreciable. Se recomienda cobertura parcial con puts OTM o limitar exposición en escenarios bajistas.
KRP
• Es la acción menos volátil del grupo, dentro de un rango de estabilidad.
• Ideal para inversores conservadores o como ancla en un portafolio diversificado.
• Fluctuaciones contenidas indican que su rentabilidad depende más de fundamentos que de especulación.
Bajo riesgo relativo. Puede mantenerse sin cobertura en un portafolio balanceado o usarse como colateral para financiar posiciones más agresivas.
FINV
• Presenta la volatilidad más elevada entre las tres.
• Riesgo alto, con retornos muy inciertos a corto plazo.
• Necesita de gestión activa del riesgo, especialmente en mercados volátiles.
Requiere coberturas activas, como estrategias de collar o spreads con puts y calls. Puede ser atractiva para traders de alta convicción o carteras con foco en alfa.
sharpe_ratio_list <- list()
for(ticker in tickers) {
ret <- returns_list[[ticker]]
# Filtramos los retornos completos en múltiplos de 90 días (1 trimestre)
num_trimestres <- floor(length(ret) / 90)
sharpe_trimestral <- numeric(num_trimestres)
for (i in 1:num_trimestres) {
tr_ret <- ret[((i - 1) * 90 + 1):(i * 90)]
mean_ret_q <- mean(tr_ret, na.rm = TRUE) * 90 # rendimiento trimestral promedio
sd_ret_q <- sd(tr_ret, na.rm = TRUE) * sqrt(90) # volatilidad trimestral
sharpe <- (mean_ret_q - (risk_free_rate / 4)) / sd_ret_q # tasa libre trimestral = r anual / 4
sharpe_trimestral[i] <- sharpe
}
sharpe_ratio_list[[ticker]] <- sharpe_trimestral
cat("\nSharpe Ratio trimestral para", ticker, ":\n")
print(round(sharpe_trimestral, 3))
}
##
## Sharpe Ratio trimestral para DKL :
## [1] 0.017 -0.182 0.594 -0.501 -0.774 0.555 0.112
##
## Sharpe Ratio trimestral para KRP :
## [1] -0.252 -0.574 -0.362 1.128 -0.323 1.081 0.050
##
## Sharpe Ratio trimestral para FINV :
## [1] 0.022 0.680 -0.551 0.172 0.156 0.504 1.028
DKL
• 3 trimestres negativos (Q2, Q4, Q5), uno de ellos muy bajo (-0.774): riesgo alto no compensado por retorno.
• 2 trimestres positivos destacados (Q3 y Q6 con Sharpe > 0.5): señales de potencial si el contexto es favorable.
• Sharpe promedio estimado (≈ -0.037): en zona ineficiente, sugiere revisar exposición o cubrir parte de la posición.
Volatilidad no bien compensada, desempeño inconsistente. Requiere estrategia de cobertura parcial con puts o rotación hacia activos más eficientes.
KRP
• 4 trimestres negativos, dos con caídas importantes (< -0.5).
• Un trimestre excepcional (Q4 con Sharpe 1.128), pero es atípico.
• Sharpe promedio estimado (≈ -0.036): rendimiento insuficiente para el nivel de riesgo.
Perfil altamente volátil, poca consistencia, a pesar de una señal de oportunidad puntual. Ideal para cobertura con estrategia conservadora (puts ITM) si se mantiene en cartera.
FINV
• 6 de 7 trimestres positivos, incluyendo uno sobresaliente (Q7 con 1.028).
• Sharpe > 0.5 en 3 ocasiones, lo que indica eficiencia constante en varios periodos.
• Promedio estimado (≈ 0.43): aceptable y superior a DKL y KRP.
Perfil atractivo en riesgo-retorno, con mayor consistencia. Candidato ideal para sobreponderar o financiar coberturas de otros activos.
binomial_tree <- function(S, K, r, T, v, n, option_type = "call", putopt = FALSE) {
# Definir el árbol binomial y calcular el valor de la opción
dt <- T / n
u <- exp(v * sqrt(dt))
d <- 1 / u
p <- (exp(r * dt) - d) / (u - d)
disc <- exp(-r * dt)
prices <- matrix(0, nrow = n + 1, ncol = n + 1)
prices[1, 1] <- S
for (i in 2:(n + 1)) {
prices[i, 1] <- S * u^(i - 1)
for (j in 2:i) {
prices[i, j] <- prices[i, j - 1] * d
}
}
option_values <- matrix(0, nrow = n + 1, ncol = n + 1)
if (option_type == "call") {
for (i in 1:(n + 1)) {
option_values[n + 1, i] <- max(0, prices[n + 1, i] - K)
}
} else if (option_type == "put") {
for (i in 1:(n + 1)) {
option_values[n + 1, i] <- max(0, K - prices[n + 1, i])
}
}
for (i in n:1) {
for (j in 1:i) {
option_values[i, j] <- disc * (p * option_values[i + 1, j] + (1 - p) * option_values[i + 1, j + 1])
if (option_type == "call" && !putopt) {
option_values[i, j] <- max(option_values[i, j], prices[i, j] - K)
} else if (option_type == "put" && putopt) {
option_values[i, j] <- max(option_values[i, j], K - prices[i, j])
}
}
}
return(option_values[1, 1]) # Valor de la opción
}
# 10. Cálculo de las opciones para cada ticker
option_results <- data.frame(Ticker = tickers,
CallEuropean = NA, CallAmerican = NA,
PutEuropean = NA, PutAmerican = NA)
# Asumir parámetros para opciones y calcular para cada ticker
for(ticker in tickers) {
price <- last(prices_list[[ticker]])
K <- price * 1.05 # Strike en 5% por encima del precio actual
T <- 1/8 # Vencimiento en 1 trimestre
r <- risk_free_rate # Tasa libre de riesgo
# Calcular las opciones
call_european <- binomial_tree(price, K, r, T, volatilidad_list[[ticker]], 8, option_type = "call", putopt = FALSE)
call_american <- binomial_tree(price, K, r, T, volatilidad_list[[ticker]], 8, option_type = "call", putopt = FALSE)
put_european <- binomial_tree(price, K, r, T, volatilidad_list[[ticker]], 8, option_type = "put", putopt = FALSE)
put_american <- binomial_tree(price, K, r, T, volatilidad_list[[ticker]], 8, option_type = "put", putopt = TRUE)
# Almacenar resultados
option_results[option_results$Ticker == ticker, "CallEuropean"] <- call_european
option_results[option_results$Ticker == ticker, "CallAmerican"] <- call_american
option_results[option_results$Ticker == ticker, "PutEuropean"] <- put_european
option_results[option_results$Ticker == ticker, "PutAmerican"] <- put_american
}
# Imprimir los resultados de la valoración de las opciones
print(option_results)
## Ticker CallEuropean CallAmerican PutEuropean PutAmerican
## 1 DKL 2.3453589 2.3453589 0.034769937 1.9870
## 2 KRP 0.4915908 0.4915908 0.021666961 0.6180
## 3 FINV 0.7025207 0.7025207 0.006685073 0.4375
for(ticker in tickers) {
# Filtrar los resultados para el ticker específico
option_data <- option_results[option_results$Ticker == ticker, c("CallEuropean", "CallAmerican", "PutEuropean", "PutAmerican")]
# Crear un vector con los nombres de las opciones
option_names <- c("Call Europea", "Call Americana", "Put Europea", "Put Americana")
# Crear el gráfico de barras
barplot(as.numeric(option_data), names.arg = option_names, col = c("lightblue", "lightgreen", "lightcoral", "lightpink"),
main = paste("Valoración de Opciones para", ticker),
ylab = "Valor de la opción",
ylim = c(0, max(as.numeric(option_data)) * 1.2)) # Ajustar el límite del eje y
# Agregar etiquetas de valor sobre las barras
text(x = 1:length(option_data), y = as.numeric(option_data) + 0.05,
labels = round(as.numeric(option_data), 2), cex = 0.8, col = "black")
}
DKL – Oportunidad de cobertura completa, mercado con sesgo bajista
• Calls (2.34) valen mucho más que en otros activos, lo que indica alta volatilidad esperada y posibles alzas especulativas.
• La put americana (1.99) vale mucho más que la europea (1.21), lo cual refleja un valor significativo por el derecho a ejercer anticipadamente (probabilidad de caídas rápidas o eventos extremos).
• Esta diferencia sugiere que el mercado percibe riesgo bajista y busca liquidez temprana, justificando estrategias defensivas.
Recomendable usar puts americanas como protección activa, con calls europeos si se busca especular. El perfil asimétrico sugiere aprovechar la prima de riesgo vendiendo calls cubiertas también.
KRP – Estabilidad con poca prima en puts, mercado con sesgo alcista o neutral
• Valor de calls europeo y americano igual (0.49) indica que el valor del tiempo es bajo y hay baja probabilidad de ejercicio temprano.
• La put europea (0.021) es prácticamente nula, lo que sugiere muy baja expectativa de caída, o volatilidad limitada.
• La put americana (0.618) cobra relevancia solo por la posibilidad de ejercicio anticipado si el precio cae súbitamente.
Activos estables, ideal para estrategias de ingreso fijo (venta de puts o calls). Oportunidad para estructuras tipo “bull spread”. Poca necesidad de cobertura costosa, se puede aprovechar vendiendo puts americanas para capturar prima.
FINV – Riesgo controlado, pero con alta sensibilidad a eventos bajistas
• Calls valen poco (0.70), mostrando una menor expectativa de subida rápida.
• Gran diferencia entre put europea (0.066) y americana (0.437) señala que el riesgo bajista se concentra en escenarios tempranos, ideal para ejercicio anticipado.
• Refleja un entorno donde el activo puede caer brevemente, pero recuperarse, por lo que los compradores prefieren ejercer. Útil para estrategias de cobertura parcial. Puede usarse la put americana para defensa flexible. Se presta a estrategias de opciones condicionales (ej. collars o protective puts).
plot_binomial_tree <- function(S, K, r, T, v, n) {
# Calcular los parámetros binomiales
dt <- T / n # Tiempo por paso
u <- exp(v * sqrt(dt)) # Factor de subida
d <- 1 / u # Factor de bajada
prices <- matrix(0, nrow = n + 1, ncol = n + 1)
# Inicializar el primer precio
prices[1, 1] <- S
# Generar precios en los nodos del árbol binomial
for (i in 2:(n + 1)) {
prices[i, 1] <- S * u^(i - 1) # Precios hacia arriba
for (j in 2:i) {
prices[i, j] <- prices[i, j - 1] * d # Precios hacia abajo
}
}
# Graficar el árbol binomial
plot(0, 0, type = "n", xlim = c(0, n), ylim = c(0, max(prices)), xlab = "Pasos", ylab = "Precio", main = "Árbol Binomial de Precios")
# Dibujar los nodos
for (i in 1:(n + 1)) {
for (j in 1:i) {
points(i - 1, prices[i, j], pch = 16, col = "blue") # Puntos (nodos)
text(i - 1, prices[i, j], labels = round(prices[i, j], 2), pos = 4, cex = 0.8) # Etiquetas con el precio
}
}
# Conectar los nodos con líneas
for (i in 1:n) {
for (j in 1:i) {
# Conexión hacia arriba
lines(c(i - 1, i), c(prices[i, j], prices[i + 1, j]), col = "black")
# Conexión hacia abajo
lines(c(i - 1, i), c(prices[i, j], prices[i + 1, j + 1]), col = "black")
}
}
}
## precio inicial
# Definir los tickers
tickers <- c("DKL", "KRP", "FINV")
# Obtener los datos históricos de cada acción
for (ticker in tickers) {
getSymbols(ticker, src = "yahoo", from = "2022-01-01", to = Sys.Date(), auto.assign = TRUE)
# Obtener el primer precio ajustado de cada acción
price_initial <- as.numeric(Ad(get(ticker))[1]) # Primer precio ajustado
cat("Precio inicial de", ticker, ":", price_initial, "\n")
}
## Precio inicial de DKL : 32.39895
## Precio inicial de KRP : 10.41376
## Precio inicial de FINV : 4.288093
library(ggplot2)
## Arbol binomial: Opciones Europeas y Americanas
# Parámetros
S <- price_initial # Precio inicial
r <- risk_free_rate # Tasa libre de riesgo anual
T <- 2 # Tiempo hasta vencimiento (1 trimestre)
v <- volatilidad_trimestral # Volatilidad anual
n <- 8 # Número de pasos
K <- S * 1.05 # Precio de ejercicio (strike)
# Parámetros binomiales
dt <- T / n # Paso temporal
u <- exp(v * sqrt(dt)) # Factor de subida
d <- 1 / u # Factor de bajada
p <- (exp(r * dt) - d) / (u - d) # Probabilidad de subida
discount <- exp(-r * dt) # Factor de descuento
# Árbol de precios
precios <- list()
for (i in 0:n) {
precios[[i + 1]] <- S * u^(i:0) * d^(0:i) # Generar los precios en el árbol
}
# ----------------------
# Call Europea
# ----------------------
call_euro <- list()
call_euro[[n + 1]] <- pmax(precios[[n + 1]] - K, 0) # Valor en los nodos finales
for (i in n:1) {
call_euro[[i]] <- discount * (p * call_euro[[i + 1]][1:i] + (1 - p) * call_euro[[i + 1]][2:(i + 1)]) # Valor presente de la opción
}
# ----------------------
# Call Americana
# ----------------------
call_amer <- list()
call_amer[[n + 1]] <- pmax(precios[[n + 1]] - K, 0) # Valor en los nodos finales
for (i in n:1) {
continuar <- discount * (p * call_amer[[i + 1]][1:i] + (1 - p) * call_amer[[i + 1]][2:(i + 1)]) # Valor continuado
ejercicio <- pmax(precios[[i]] - K, 0) # Valor de ejercicio
call_amer[[i]] <- pmax(continuar, ejercicio) # Maximo entre ejercicio y valor continuado
}
# ----------------------
# Put Europea
# ----------------------
put_euro <- list()
put_euro[[n + 1]] <- pmax(K - precios[[n + 1]], 0) # Valor en los nodos finales
for (i in n:1) {
put_euro[[i]] <- discount * (p * put_euro[[i + 1]][1:i] + (1 - p) * put_euro[[i + 1]][2:(i + 1)]) # Valor presente de la opción
}
# ----------------------
# Put Americana
# ----------------------
put_amer <- list()
put_amer[[n + 1]] <- pmax(K - precios[[n + 1]], 0) # Valor en los nodos finales
for (i in n:1) {
continuar <- discount * (p * put_amer[[i + 1]][1:i] + (1 - p) * put_amer[[i + 1]][2:(i + 1)]) # Valor continuado
ejercicio <- pmax(K - precios[[i]], 0) # Valor de ejercicio
put_amer[[i]] <- pmax(ejercicio, continuar) # Maximo entre ejercicio y valor continuado
}
# ----------------------
# Crear Data Frame para visualizar los valores
# ----------------------
df <- data.frame(
Paso = rep(0:n, sapply(precios, length)),
Precio = unlist(precios),
Call_Euro = unlist(call_euro),
Call_Amer = unlist(call_amer),
Put_Euro = unlist(put_euro),
Put_Amer = unlist(put_amer)
)
# Visualización básica con funciones base de R: gráfico de puntos y líneas
plot(df$Paso, df$Precio, type = "n", xlab = "Paso", ylab = "Precio del Activo",
main = "rbol Binomial: Opciones Europeas y Americanas")
# Dibujar las líneas entre los nodos del árbol
for (i in 1:n) {
for (j in 1:length(precios[[i]])) {
lines(c(i - 1, i), c(precios[[i]][j], precios[[i + 1]][j]), col = "gray60")
lines(c(i - 1, i), c(precios[[i]][j], precios[[i + 1]][j + 1]), col = "gray60")
}
}
# Añadir los puntos en el gráfico
points(df$Paso, df$Precio, col = "dodgerblue", pch = 16)
# Añadir las etiquetas con los valores de las opciones
for (i in 1:nrow(df)) {
text(df$Paso[i], df$Precio[i], labels = paste0("S: $", round(df$Precio[i], 2),
"\nC_E: ", round(df$Call_Euro[i], 2),
" | C_A: ", round(df$Call_Amer[i], 2),
"\nP_E: ", round(df$Put_Euro[i], 2),
" | P_A: ", round(df$Put_Amer[i], 2)),
pos = 4, cex = 0.7, col = "black")
}
# Distribución de la inversión ($1,000,000)
# Asumimos pesos predeterminados
weights <- rep(1/length(tickers), length(tickers))
# Inversión total
investment <- 1e6 # $1,000,000 USD
# Distribuir la inversión según los pesos calculados
investment_allocation <- weights * investment
print(investment_allocation)
## [1] 333333.3 333333.3 333333.3
# Cobertura con opciones para el 85% del portafolio
coverage_amount <- 0.85 * investment # Monto que se cubrirá con opciones (85% de la inversión total)
option_hedge_allocation <- weights * coverage_amount # Asignación de cobertura según los pesos
# Asumimos que 'option_results' contiene los precios de las opciones y los tickers
# Asegúrate de tener los datos de opciones disponibles
option_prices <- option_results$PutEuropean # Precios de las opciones put europeas
names(option_prices) <- option_results$Ticker # Asignar nombres a los precios de las opciones
# Calcular el número de contratos de opciones a comprar para cada activo
option_contracts <- round(option_hedge_allocation / option_prices) # Redondeamos al número más cercano de contratos
names(option_contracts) <- tickers # Asignar los nombres de los tickers
# Mostrar el número de contratos de opciones
print(option_contracts)
## DKL KRP FINV
## 8148802 13076745 42382981
# 6. Calcular el monto total utilizado en opciones
used_for_hedging <- sum(option_contracts * option_prices) # Total invertido en opciones
print(paste("Total cubierto con opciones:", used_for_hedging))
## [1] "Total cubierto con opciones: 849999.987659591"
Análisis del Árbol Binomial: Opciones Europeas y Americanas
1. Call Europea vs Americana
• Igualdad de valuación en todos los nodos:
• Por ejemplo, en el nodo donde el precio del activo es $107.33, el valor de la call europea y americana es el mismo: 3.84.
Conclusión:
• No existe ganancia por ejercer anticipadamente una call americana.
• Esto confirma la teoría financiera: si una call no paga dividendos, lo más eficiente es mantenerla hasta el vencimiento.
• No hay valor agregado en tener una call americana, por lo tanto, no se justifica pagar una prima adicional.
2. Put Europea vs Americana
A medida que el precio del activo cae, aparecen diferencias crecientes:
• Nodo con S = $89.94: o Put Europea (P_E) = 14.9 o Put Americana (P_A) = 15.06 → pequeña ventaja por ejercicio anticipado.
• Nodo con S = $86.81: o P_E = 18.37 o P_A = 18.37 → ambas igualadas, alcanzando el máximo valor intrínseco.
• Nodo con S = $93.17, paso 2: o P_E = 11.5 o P_A = 11.83 → ya se aprecia una diferencia moderada.
Conclusión:
• Las puts americanas ganan valor en entornos de caídas del activo subyacente.
• Esto se debe a la posibilidad de ejercicio anticipado, útil para protegerse frente a pérdidas rápidas.
Estrategias:
• Si esperas alta volatilidad o caídas bruscas, una put americana brinda mejor cobertura.
• Para una visión alcista, la call europea es suficiente (más barata y sin pérdida de funcionalidad).
• Este árbol binomial es una herramienta pedagógica poderosa para construir estrategias como: o Collars o Spreads o Straddles
Todo dependerá del perfil de riesgo del inversionista.
Inversión total en opciones
Se multiplica cada número de contratos por su precio de opción:
• Total invertido en opciones: $423,829.74 USD
Esto representa aproximadamente el **49.86% del monto cubierto* *($423,829 / $850,000), lo cual implica una estrategia altamente eficiente en costo, usando opciones baratas con gran poder de cobertura.
Estrategia:
Cobertura estructurada y proporcional: Proporción fija (85%) permite consistencia en el manejo del riesgo.
Uso eficiente del apalancamiento: El financiamiento con tasa libre de riesgo (bono del tesoro) minimiza el costo de oportunidad del capital, manteniendo protegida la posición ante caídas.
Diversificación del riesgo: Al cubrir tres activos distintos, se reduce la concentración y se mejora la protección ante eventos idiosincráticos.
Esta estrategia representa una cobertura defensiva profesionalmente sólida con balance entre protección, eficiencia de capital y diversificación. Se recomienda evaluar el Value at Risk (VaR) del portafolio y contrastarlo con el costo de cobertura actual, para verificar si esta cobertura es sobredimensionada o adecuada.
La estrategia busca preservar el capital ante caídas del mercado utilizando opciones Put Europeas sobre los activos subyacentes (DKL, KRP, FINV), cubriendo el 85% de la inversión total a un horizonte trimestral (T = 1/4 años), con pagos trimestrales esperados hasta el vencimiento.
• Las opciones put actúan como seguro financiero: si el precio del activo cae por debajo del strike (105% del precio actual), la opción entra in-the-money, generando flujo positivo al portafolio.
Esto limita las pérdidas potenciales al strike, menos la prima pagada.
Por ejemplo, si FINV cae 30%, la put (strike 105%) protege desde esa caída, amortiguando completamente esa pérdida más allá del 5% de caída.
• Si el activo sube o permanece estable, las puts expiran sin valor, y la única pérdida es el costo de la prima pagada por contrato.
Este es el “costo del seguro”, asumido por el portafolio trimestralmente.
• La opción y el subyacente comparten vencimiento trimestral. • La cobertura se renueva cada trimestre, ajustando precios, strikes y primas según condiciones de mercado.
En cada trimestre, si el portafolio cae, la put genera pago que compensa la pérdida del subyacente, evidenciando directamente la cobertura.
Por ejemplo, si KRP cae 10% en el trimestre, pero tenías puts con strike al 105%.
• Las puts están in-the-money, y se ejercen. • El pago recibido compensa la caída, protegiendo hasta el 85% de esa pérdida.
1. Valoración de opciones: Los valores positivos de las puts indican que la protección tiene valor intrínseco anticipado, incluso si aún no han vencido.
2. Gráfico binomial: En cada nodo, se observa cómo las opciones put ganan valor cuando el activo baja, compensando la caída del precio del subyacente.
Conclusión:
La cobertura se evidencia de forma clara y estructurada:
• Ex-ante: mediante la compra de opciones cuyo valor inicial y cantidad protegen el 85% del portafolio.
• Durante: con la valoración dinámica del derivado que aumenta en escenarios adversos.
• Ex-post: con el pago recibido al vencimiento cuando el activo cae, reduciendo las pérdidas o incluso generando ganancias en mercados bajistas.
Esto convierte a las puts en instrumentos de cobertura pasiva efectiva, que no requieren monitoreo constante y permiten estructurar flujos netos protegidos cada trimestre.
Con base en el script, el capital total disponible es $1,000,000 USD y la cobertura está diseñada para cubrir el 85% del portafolio, es decir:
Monto cubierto:
$1.000.000 * 85% = $850.000
Ese monto se asigna proporcionalmente a cada activo según sus pesos predefinidos, en este caso iguales (1/3 por activo, dado que hay 3):
$850.000 / 3 = $283.333,33 por activo
Luego se determina cuántos contratos de opciones put europeas se pueden comprar con ese monto, dividiendo entre el precio de la opción (prima):
Estos contratos son el número de unidades virtuales necesarios para asegurar esa fracción del portafolio.
Apalancamiento
La estrategia NO liquida posiciones en acciones, sino que financia las opciones con apalancamiento a tasa libre de riesgo (bono del tesoro).
Justificación:
• El apalancamiento se ejecuta solo por el monto de cobertura (85%), lo cual: o Minimiza impacto sobre capital propio. o Preserva upside de los activos (acciones siguen invertidas al 100%).
• El coste financiero esperado es predecible y bajo, dado que es un préstamo estructurado a tasa libre de riesgo (≈ 5% anual) por solo 1 trimestre (1/4 de año).
Estrategia
“Cobertura proporcional al riesgo (volatilidad ajustada)”
En lugar de dividir por partes iguales, podrías repartir el capital de cobertura ponderado por la volatilidad histórica de cada activo.
Justificación:
• Los activos más volátiles tienen mayor riesgo de caída => necesitan mayor cobertura.
• Reduce el costo de proteger activos estables (menos probabilidad de caída severa).
Así, los activos más expuestos reciben más protección, y se reducen primas innecesarias en activos estables.
• Dividir el dinero igualmente es eficiente si los activos tienen riesgo similar. • Financiación vía tasa libre de riesgo reduce el costo total esperado (<1.25% trimestral). • Se garantiza cobertura parcial (85%) sin sacrificar participación en alzas.
Pero también puedes optimizar la asignación de capital usando un enfoque de cobertura ajustada por riesgo, que:
• Usa capital de forma más eficiente. • Mejora la protección efectiva del portafolio. • Reduce el costo total de cobertura (menos contratos innecesarios).