La práctica consiste en la estructuración de un portfolio de inversión entre 3 activos, en donde se realiza medidas de rentabilidad y riesgo para simular, mediante el Movimiento Browniano Geométrico, los precios diarios hasta los próximos dos años. Mediante el análisis del riesgo se plantea hacer uso de derivados con opciones tanto europeas como americanas para cubrir caídas de precios con una metodología basada en precios de cierre a trimestre.
library(tidyquant)
library(plotly)
library(timetk)
library(tidyr)
library(forcats)
library(tidyverse)
library(ggplot2)
library(reshape2)
Se eligieron las acciones Cheniere Energy, Inc. (LNG), Meta Platforms, Inc. (META) y NMI Holdings, Inc. (NMIH) para la construcción del portafolio. Se tomará información desde 01/06/2022 hasta el 31/03/2025
tick <- c('LNG', 'NMIH', 'META')
price_data <- tq_get(tick,from = '2022-06-01',to = '2025-03-31',get = 'stock.prices')
Se eligieron dichas acciones porque la idea de construir un portafolio es hacerlo con acciones que se encuentren subvaloradas en el mercado, con la expectativa de que su precio pueda subir en el corto plazo. Para compensar en términos de métricas rendimientos del portafolio, se decide agregar estratégicamente la acción Meta Platforms, Inc. (META) para que compense su comportamiento a la alza el comportamiento moderado o a la baja de las otras dos acciones.
#Graficar serie historica de precios
df_price <- price_data %>%
select(symbol, date, adjusted) %>%
pivot_wider(names_from = symbol, values_from = adjusted) %>%
arrange(date)
# Ajustar los márgenes para dar espacio a la leyenda
par(mar = c(5, 4, 4, 8), xpd = TRUE)
# Graficar con matplot
matplot(df_price[, -1], type = "l", lty = 1, col = 1:(ncol(df_price) - 1))
# Agregar la leyenda fuera del gráfico
legend("topright", inset = c(-0.3, 0), legend = colnames(df_price)[-1],
col = 1:(ncol(df_price) - 1), lty = 1, bty = "n")
#Calcular rendimientos diarios
rendimientos_RCC <- price_data %>%
group_by(symbol) %>%
tq_transmute(select = adjusted, mutate_fun = periodReturn, period = 'daily',
col_rename = 'RCC', type = 'log')
#Visualizar datos
head(rendimientos_RCC)
## # A tibble: 6 × 3
## # Groups: symbol [1]
## symbol date RCC
## <chr> <date> <dbl>
## 1 LNG 2022-06-01 0
## 2 LNG 2022-06-02 0.0239
## 3 LNG 2022-06-03 -0.00988
## 4 LNG 2022-06-06 -0.0138
## 5 LNG 2022-06-07 0.0436
## 6 LNG 2022-06-08 -0.0282
Es conveniente organizar los rendimientos agrupandolos por acciones mediante columnas
#Ordenar acciones de filas a columnas
RCC_ordenado <- rendimientos_RCC %>%
pivot_wider(names_from = symbol, values_from = RCC)
#Calcular promedio y desviación diaria de los rendimientos
mean_daily <- colMeans(RCC_ordenado[, -1], na.rm = TRUE)
print(mean_daily)
## LNG NMIH META
## 0.0007159259 0.0009306345 0.0015828675
sd_daily <- apply(RCC_ordenado[, -1], 2, sd, na.rm = TRUE)
print(sd_daily)
## LNG NMIH META
## 0.01831020 0.01701104 0.02763702
La idea de calcular los rendimientos y su riesgo tiene dos propósitos:
Conocer estas métricas individualmente para identificar como se comportan a diario y lo que debe de tener en cuenta el inversionista día a día
Anualizar el promedio de los rendimientos y el riesgo para calcular el Ratio Sharpe, lo cual nos permitirá construir el portafolio de media varianza (maximización del Ratio Sharpe)
#Calcular matriz de covarianzas y anualizar
RCC_ordenado<-RCC_ordenado[,-1]
cov_mat <- cov(RCC_ordenado) * 252
print(round(cov_mat,4))
## LNG NMIH META
## LNG 0.0845 0.0213 0.0058
## NMIH 0.0213 0.0729 0.0266
## META 0.0058 0.0266 0.1925
Para el cálculo del Ratio Sharpe se debe de tener en cuenta la tasa libre riesgo basada en los bonos del tesoro Tbond, el cual a fecha del 31/03/2025 se encontraba a 1,38% en términos anuales con capitalización semestral, por lo cual se debía hacer una conversión para manejarlo en términos EA.
#Calcular pesos aleaorios de inversion
pesos <- runif(n = length(tick))
print(pesos)
## [1] 0.8061071 0.6377263 0.7159452
#La suma de los pesos debe de ser igual a 1
pesos<-pesos/sum(pesos)
print(pesos)
## [1] 0.3732360 0.2952739 0.3314901
#Calcular rendimientos anualizados del portafolio
rend_portafolio <- (sum(pesos * mean_daily) + 1)^252 - 1
#Calcular riesgo del portafolio
riesgo_portafolio<-sqrt(t(pesos) %*% (cov_mat %*% pesos))
#Calculo del ratio sharpe
tbond_5yr=0.0138
rf<-(1+(tbond_5yr/2))^2-1
ratio_sharpe<-(rend_portafolio-rf)/riesgo_portafolio
print(ratio_sharpe)
## [,1]
## [1,] 1.308736
La maximización de este ratio nos da como resultado la mejor combinación retorno-riesgo y se encuentra en la frontera eficiente. Este ratio lo que hace es maximizar el rendimiento esperado para cierto nivel de riesgo.
#Crear matriz y vectores vacios
num_port <- 5000
pesos1 <- matrix(nrow = num_port, ncol = length(tick))
port_returns <- vector('numeric', length = num_port)
port_risk <- vector('numeric', length = num_port)
sharpe_ratio <- vector('numeric', length = num_port)
#Generar bucle
for (i in seq_along(port_returns)) {
pesos <- runif(length(tick))
pesos <- pesos/sum(pesos)
#Almacenar peso en la matriz
pesos1[i,] <- pesos
#Retorno del portafolio
port_ret <- sum(pesos * mean_daily)
port_ret <- ((port_ret + 1)^252) - 1
#Almacenar retorno del portafolio
port_returns[i] <- port_ret
#Crear y almacenar riesgo del portafolio
port_sd <- sqrt(t(pesos) %*% (cov_mat %*% pesos))
port_risk[i] <- port_sd
#Crear y almacenar riesgo del portafolio
sr <- (port_ret-rf)/port_sd
sharpe_ratio[i] <- sr
}
#Almacenar valores en una tabla
portfolio_values <- tibble(Return = port_returns,
Risk = port_risk,
SharpeRatio = sharpe_ratio)
#Convertir una matriz en un tibble y cambiar los nombres de las columnas
pesos1 <- tk_tbl(pesos1)
colnames(pesos1) <- colnames(RCC_ordenado)
#Combinar todos los valores juntos
portfolio_values <- tk_tbl(cbind(pesos1, portfolio_values))
head(portfolio_values)
## # A tibble: 6 × 6
## LNG NMIH META Return Risk SharpeRatio
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0.0686 0.654 0.278 0.318 0.241 1.26
## 2 0.190 0.523 0.287 0.312 0.227 1.31
## 3 0.512 0.318 0.170 0.264 0.214 1.17
## 4 0.177 0.253 0.570 0.375 0.284 1.27
## 5 0.160 0.496 0.344 0.326 0.236 1.32
## 6 0.508 0.116 0.376 0.308 0.239 1.23
La elección estratégica de las 3 acciones que conforman el portafolio permitió al maximizar el Ratio Sharpe, obtener ponderaciones equilibradas entre los activos, lo cuál es beneficioso para el término de la diversificación y lo cual tiene como efecto una menor exposición al riesgo.
#Portafolio de media varianza (Maximizar ratio sharpe)
max_sr <- portfolio_values[which.max(portfolio_values$SharpeRatio),]
p_media_varianza <- max_sr %>%
gather(LNG:META, key = Asset,
value = Weights) %>%
mutate(Asset = as.factor(Asset)) %>%
ggplot(aes(x = fct_reorder(Asset,Weights), y = Weights, fill = Asset)) +
geom_bar(stat = 'identity') +
theme_minimal() +
labs(x = 'Acciones', y = 'Pesos', title = "Portafolio de media varianza") +
scale_y_continuous(labels = scales::percent)
print(max_sr)
## # A tibble: 1 × 6
## LNG NMIH META Return Risk SharpeRatio
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0.242 0.397 0.361 0.324 0.233 1.33
ggplotly(p_media_varianza)
La frontera eficiente es el límite de la curva que nos permite visualizar la cantidad infinita de combinaciones óptimas para estructurar el portafolio.
Al maximizar el Ratio Sharpe, significa que por cada unidad de riesgo, recibimos 1.3 en exceso de retorno. Esta medida es considerada balanceada y eficiente ya que el exceso de retorno obtenido es mayor al nivel de riesgo que se asume.
#Graficar frontera eficiente
frontera_eficiente <- portfolio_values %>%
ggplot(aes(x = Risk, y = Return, color = SharpeRatio)) +
geom_point() +
theme_classic() +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(labels = scales::percent) +
labs(x = 'Riesgo anualizado',
y = 'Retorno anualizado',
title = "Frontera eficiente") +
geom_point(aes(x = Risk,
y = Return), data = max_sr, color = 'red') +
annotate('text', x = 0.2, y = 0.42, label = "Max_Sharpe") +
annotate(geom = 'segment', x = 0.22, xend = 0.2275, y = 0.405,
yend = 0.365, color = 'red', arrow = arrow(type = "open"))
ggplotly(frontera_eficiente)
Hasta este punto se ha estructurado un portafolio de media-varianza, que cuenta con una distribución equilibrada en sus ponderaciones de invesión. Pero si comparamos la tasa libre de riesgo a comparación del riesgo que se asume en cada activo, por ejemplo en un activo como META que presenta mayor volatilidad, se presenta un riesgo de factores del mercado que hacen que el portafolio tenga un nivel de riesgo elevado (23.3% anual). Este valor según como se analice puede estar por debajo de volatilidades de otros activos, como también puede verse muy elevado y más si alguien no quiere asumir ese nivel de riesgo para obtener la rentabilidad ofrecida.
Teniendo en cuenta que se pueden calcular rentabilidades y riesgos anualizados para cada activo, es interesante conocer a partir de una simulación por medio del Movimiento Browniano Geomtétrico, para hacer un análisis de sensibilidad extremo de la evolución del precio diario del activo.
La inversión en este caso está contemplada para 2 años y se tomará el tiempo pensando en días bursátiles y para el ejercicio de simulación de precios de cada activo se hará con 5000 simulación de escenarios diferentes.
year<-252
T=504
N<-5000
dt<-1/year
#MBG para LNG
mean_LNG<-mean(RCC_ordenado$LNG)
rendimiento_LNG <- exp(mean_LNG * year) - 1
sigma_LNG=sqrt(var(RCC_ordenado$LNG)*year)
S0_LNG<-tail(df_price$LNG, 1)
MBG_LNG <- matrix(NA, nrow = T, ncol = N)
MBG_LNG[1, ] <- S0_LNG
for (i in 1:N) {
for (t in 2:T) {
MBG_LNG[t, i] <- MBG_LNG[t - 1, i] * exp((rendimiento_LNG - 0.5 * sigma_LNG^2) * dt +
sigma_LNG * sqrt(dt) * qnorm(runif(1)))
}
}
matplot(MBG_LNG,type="l")
#MBG para META
mean_META<-mean(RCC_ordenado$META)
rendimiento_META <- exp(mean_META * year) - 1
sigma_META=sqrt(var(RCC_ordenado$META)*year)
S0_META<-tail(df_price$META, 1)
MBG_META <- matrix(NA, nrow = T, ncol = N)
MBG_META[1, ] <- S0_META
for (i in 1:N) {
for (t in 2:T) {
MBG_META[t, i] <- MBG_META[t - 1, i] * exp((rendimiento_META - 0.5 * sigma_META^2) * dt +
sigma_META * sqrt(dt) * qnorm(runif(1)))
}
}
matplot(MBG_META,type="l")
#MBG para NMIH
mean_NMIH<-mean(RCC_ordenado$NMIH)
rendimiento_NMIH <- exp(mean_NMIH * year) - 1
sigma_NMIH=sqrt(var(RCC_ordenado$NMIH)*year)
S0_NMIH<-tail(df_price$NMIH, 1)
MBG_NMIH <- matrix(NA, nrow = T, ncol = N)
MBG_NMIH[1, ] <- S0_NMIH
for (i in 1:N) {
for (t in 2:T) {
MBG_NMIH[t, i] <- MBG_NMIH[t - 1, i] * exp((rendimiento_NMIH - 0.5 * sigma_NMIH^2) * dt +
sigma_NMIH * sqrt(dt) * qnorm(runif(1)))
}
}
matplot(MBG_NMIH,type="l")
La idea de hacer esta simulación en cada una de las acciones del portafolio, es tomar un escenario en donde el precio de la acción esté cayendo y con esto simular como se comporta el portafolio en cada cierre de precios en trimestre, por lo cual se asumirá intervalos de 63 días.
# Paso 1: Encontrar la trayectoria con menor valor final
col_LNG <- which.min(MBG_LNG[nrow(MBG_LNG), ])
col_META <- which.min(MBG_META[nrow(MBG_META), ])
col_NMIH <- which.min(MBG_NMIH[nrow(MBG_NMIH), ])
# Paso 2: Extraer la trayectoria completa
trayectoria_LNG <- MBG_LNG[, col_LNG]
trayectoria_META <- MBG_META[, col_META]
trayectoria_NMIH <- MBG_NMIH[, col_NMIH]
#Analisis trimestral
T_trimestres <- seq(1, T, by = 63)
# Extraer precios de cierre trimestrales
precios_trimestrales_LNG <- trayectoria_LNG[T_trimestres]
precios_trimestrales_META <- trayectoria_META[T_trimestres]
precios_trimestrales_NMIH <- trayectoria_NMIH[T_trimestres]
# Crear un data frame con los precios trimestrales
precios_trimestrales <- data.frame(
Trimestre = 1:length(precios_trimestrales_LNG),
LNG = precios_trimestrales_LNG,
META = precios_trimestrales_META,
NMIH = precios_trimestrales_NMIH
)
pesos_mediavarianza <- as.numeric(as.vector(max_sr[, c("LNG", "META", "NMIH")]))
precios_matriz <- as.matrix(precios_trimestrales[, c("LNG", "META", "NMIH")])
La simulación se construyó para 2 años, en donde equivalen 8 trimestres. La idea ahora es saber como se comporta el portafolio ante este tipo de escenarios y sin ningún tipo de cobertura.
# Multiplicar los precios trimestrales por los pesos óptimos
valor_portafolio_trimestral_unitario <- precios_matriz %*% pesos_mediavarianza
inversion<-1000000
precios_iniciales<-c(S0_LNG,S0_META,S0_NMIH)
cantidad_acciones<-(inversion*pesos_mediavarianza)/precios_iniciales
valor_portafolio_trimestral <- precios_matriz %*% cantidad_acciones
matplot(valor_portafolio_trimestral,type="l",col="blue"
,xlab = "Trimestre",ylab = "Precio portafolio",main="Comportamiento del portafolio en un caso adverso")
En este caso extremo y que en términos de probabilidades, es poco probable que ocurra, se evidencia que ante un esecenario en donde no hay una cobertura que me proteja de las caídas de los precios, la inversión inicial que es de 1000000 USD se ve reducida por un mal desempeño del portafolio. Esto basado en terminos de la simulación que también tiene sus propios supuestos y volatilidades tan altas como la de META, generan no solo caídas sino subidas de precios elevadas en ciertas trayectorias.
Sin embargo, definiendo el término de volatilidad alta en acciones tanto individualmente como del portafolio en conjunto, demuestran que se asumen riesgos muy altos que pueden materializarse en este tipo de situaciones.
El VaR al 1% para la acción de META es de aproximadamente 300 USD, lo que indica que, en condiciones normales de mercado, solo en el 1% de los casos se esperaría una pérdida mayor a ese valor. De igual forma, el VaR al 5% es de aproximadamente 450 USD. Ambos valores representan entre el 50% y el 80% del precio actual, lo que refleja un riesgo considerable en escenarios adversos.
#META
VaR_1_META <- quantile(MBG_META[T, ], 0.01) # Cuantil al 1%
VaR_5_META <- quantile(MBG_META[T, ], 0.05) # Cuantil al 5%
print(VaR_1_META)
## 1%
## 291.8324
print(VaR_5_META)
## 5%
## 450.2695
plot(density(MBG_META[T, ]),
ylab = "", xlab = "Precio Simulado",
main = "Distribución Empírica - META",
lwd = 3, col = "blue")
abline(v = VaR_1_META, col = "blue", lwd = 2, lty = 2) # VaR al 1%
abline(v = VaR_5_META, col = "green", lwd = 2, lty = 2) # VaR al 5%
abline(v = S0_META, col = "red", lwd = 2) # Precio inicial
El VaR al 1% para la acción de LNG es de aproximadamente 100 USD, lo cual indica que, bajo condiciones normales, solo en el 1% de los casos se esperaría una pérdida mayor a ese valor. Por otro lado, el VaR al 5% es de 160 USD. Estos valores representan entre el 50% y el 70% del precio actual de la acción, lo cual evidencia una alta exposición al riesgo en escenarios extremos.
#LNG
VaR_1_LNG <- quantile(MBG_LNG[T, ], 0.01)
VaR_5_LNG <- quantile(MBG_LNG[T, ], 0.05)
print(VaR_1_LNG)
## 1%
## 126.7426
print(VaR_5_LNG)
## 5%
## 161.6258
plot(density(MBG_LNG[T, ]), main="Distribución Empírica - LNG", lwd=3, col="blue")
abline(v = VaR_1_LNG, col = "blue", lwd = 2, lty = 2)
abline(v = VaR_5_LNG, col = "green", lwd = 2, lty = 2)
abline(v = S0_LNG, col = "red", lwd = 2)
El VaR al 1% para la acción de NMIH es de aproximadamente 23 USD, lo que implica que, en condiciones normales de mercado, solo en el 1% de los casos se esperaría una pérdida mayor. En el caso del VaR al 5%, la pérdida podría alcanzar los 31 USD. Estos valores representan entre el 65% y el 84% del precio actual de la acción, reflejando una exposición significativa al riesgo bajo escenarios extremos.
#NMIH
VaR_1_NMIH <- quantile(MBG_NMIH[T, ], 0.01)
VaR_5_NMIH <- quantile(MBG_NMIH[T, ], 0.05)
print(VaR_1_NMIH)
## 1%
## 24.01029
print(VaR_5_NMIH)
## 5%
## 30.51547
plot(density(MBG_NMIH[T, ]), main="Distribución Empírica - NMIH", lwd=3, col="blue")
abline(v = VaR_1_NMIH, col = "blue", lwd = 2, lty = 2)
abline(v = VaR_5_NMIH, col = "green", lwd = 2, lty = 2)
abline(v = S0_NMIH, col = "red", lwd = 2)
Después de haber evidenciado los diferentes riesgos al simular los precios y mostrar un escenario extremo, a su vez, al demostrar las perdidas que se pueden generar a partir del portafolio dada la inversión y tambien como a nivel individual se pueden presentar casos extremos de baja probabilidad con grandes perdidas, es necesario pensar en una estrategia para cubrirse.
En los derivados existe un instrumento, el cual son las opciones y nos permite hacer coberturas ante escenarios específicos. Otra alternativa sería cambiar el portafolio por activos menos volatiles pero esto llevaría probablemente una reducción en cierta medida del rendimiento, por lo que si el portafolio es atractivo a nivel de rendimiento, pero preocupa el nivel de riesgo se puede emplear este instrumento para cubrirse.
Para hacer uso de estas opciones es importante crear las funciones que me permitirán ingresar unos parámetros de entrada. Por lo tanto, las siguientes funciones me sirven para valorar opciones tanto americanas como europeas y a su vez, construir el arbol de precios.
# Función para la valuación de una opción europea
European_option = function(S, K, N, h, r, delta, u, d, psubir, pbajar, type) {
stock_tree = stock_tree(S, N, u, d)
option_tree = matrix(0, nrow=nrow(stock_tree), ncol=ncol(stock_tree))
# Nodos terminales
if(type == "call") {
option_tree[nrow(option_tree), ] = pmax(stock_tree[nrow(stock_tree), ] - K, 0)
} else if(type == "put") {
option_tree[nrow(option_tree), ] = pmax(K - stock_tree[nrow(stock_tree), ], 0)
}
for(i in (nrow(option_tree)-1):1) {
for(j in 1:i) {
option_tree[i,j] = exp(-r * h) * (psubir * option_tree[i+1,j+1] + pbajar * option_tree[i+1,j])
}
}
return(option_tree)
}
# Función para construir el árbol de precios
stock_tree = function(S, N, u, d) {
tree = matrix(0, nrow=N+1, ncol=N+1)
for(i in 1:(N+1)) {
for(j in 1:i) {
tree[i,j] = S * u^(j-1) * d^((i-1)-(j-1))
}
}
return(tree)
}
#Función para la valuación de una opción americana
American_option = function(S, K, N, h, r, delta, u, d, psubir, pbajar, type) {
stock_tree = stock_tree(S, N, u, d)
option_tree = matrix(0, nrow = nrow(stock_tree), ncol = ncol(stock_tree))
# Nodos terminales
if (type == "call") {
option_tree[nrow(option_tree), ] = pmax(stock_tree[nrow(stock_tree), ] - K, 0)
# Revisión de ejercicio anticipado (call americana)
for (i in (nrow(option_tree)-1):1) {
for (j in 1:i) {
NO = exp(-r * h) * (psubir * option_tree[i+1, j+1] + pbajar * option_tree[i+1, j])
EX = pmax(stock_tree[i, j] - K, 0)
option_tree[i, j] = max(NO, EX)
}
}
} else if (type == "put") {
option_tree[nrow(option_tree), ] = pmax(K - stock_tree[nrow(stock_tree), ], 0)
# Revisión de ejercicio anticipado (put americana)
for (i in (nrow(option_tree)-1):1) {
for (j in 1:i) {
NO = exp(-r * h) * (psubir * option_tree[i+1, j+1] + pbajar * option_tree[i+1, j])
EX = pmax(K - stock_tree[i, j], 0)
option_tree[i, j] = max(NO, EX)
}
}
}
return(option_tree)
}
Se trabajaran con unos supuestos para la valuación de opciones para hacer cobertura de la inversión del portafolio.
La cobertura se hará sobre el 85% de la inversión
La tasa libre de riesgo se convertirá a trimestral continua
Si la acción tiene tasa de dividendos, se utilizará opción americana y en caso contrario, se usará opción europea. Esto dado por el fenómeno que se presenta cuando se pagan dividendos, ya que el precio tiende a bajar y sería óptimo ejercer anticipadamente
Se comprará en todos los casos una put para hacer un cubrimiento ante las caídas de los precios
Los precios strike estan determinados por la zona In the money, teniendo en cuenta la diferencia entre los precios bid y ask para determinar un mercado liquido, con alto interés y una menor volatilidad implícita en lo posible
rf_continua_trimestral<-log(1+rf)/4
dt<-63/252
periodos<-8
inversion_cobertura<-inversion*0.85
cantidad_acciones_cobertura<-(inversion_cobertura*pesos_mediavarianza)/precios_iniciales
valor_portafolio_trimestral_cobertura <- precios_matriz %*% cantidad_acciones_cobertura
#LNG
sigma_LNG_trimestral<-sigma_LNG*sqrt(dt)
su_LNG<-exp(sigma_LNG_trimestral*sqrt(dt))
sd_LNG<-1/su_LNG
dividendo_LNG<-0.0187/4
psubir_LNG<-(exp((rf_continua_trimestral-dividendo_LNG)*dt)-sd_LNG)/(su_LNG-sd_LNG)
pbajar_LNG<-1-psubir_LNG
peso_LNG<-pesos_mediavarianza[1]
q_LNG<-(peso_LNG*inversion_cobertura)/S0_LNG
KPut_LNG<-240
# Valuación de PUT (comprada)
opcion_put_LNG <- American_option(S0_LNG, KPut_LNG,periodos, dt, rf_continua_trimestral,
dividendo_LNG, su_LNG, sd_LNG, psubir_LNG, pbajar_LNG, "put")
tree_LNG <- stock_tree(S0_LNG, periodos, su_LNG, sd_LNG)
# Asegurar que es matriz numérica
tree_LNG <- as.matrix(tree_LNG)
print(tree_LNG)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 226.2300 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.000
## [2,] 210.3738 243.2813 0.0000 0.0000 0.0000 0.0000 0.0000 0.000
## [3,] 195.6289 226.2300 261.6179 0.0000 0.0000 0.0000 0.0000 0.000
## [4,] 181.9175 210.3738 243.2813 281.3364 0.0000 0.0000 0.0000 0.000
## [5,] 169.1671 195.6289 226.2300 261.6179 302.5412 0.0000 0.0000 0.000
## [6,] 157.3103 181.9175 210.3738 243.2813 281.3364 325.3443 0.0000 0.000
## [7,] 146.2846 169.1671 195.6289 226.2300 261.6179 302.5412 349.8660 0.000
## [8,] 136.0317 157.3103 181.9175 210.3738 243.2813 281.3364 325.3443 376.236
## [9,] 126.4973 146.2846 169.1671 195.6289 226.2300 261.6179 302.5412 349.866
## [,9]
## [1,] 0.0000
## [2,] 0.0000
## [3,] 0.0000
## [4,] 0.0000
## [5,] 0.0000
## [6,] 0.0000
## [7,] 0.0000
## [8,] 0.0000
## [9,] 404.5936
# Convertir con melt
tree_df_LNG <- melt(tree_LNG, varnames = c("T", "Nodo"), value.name = "Precio")
# Quitar ceros o valores vacíos (nodos no existentes)
tree_df_LNG <- subset(tree_df_LNG, Precio > 0)
# Agregar una columna que identifique si el precio sube o baja respecto al nodo anterior
tree_df_LNG <- tree_df_LNG[order(tree_df_LNG$T, tree_df_LNG$Nodo), ]
tree_df_LNG$Movimiento <- ifelse(tree_df_LNG$Precio >= lag(tree_df_LNG$Precio, default = tree_df_LNG$Precio[1]), "Sube", "Baja")
#Grafico del arbol binomial precios
ggplot(tree_df_LNG, aes(x = T-1, y = Precio, color = Movimiento)) +
geom_point(size = 3) +
geom_text(aes(label = round(Precio, 2)), vjust = -0.5, size = 3) +
geom_line(aes(group = T), color = "gray") +
scale_color_manual(values = c("Sube" = "green3", "Baja" = "red3")) +
labs(title = "Árbol Binomial de Precios - LNG", x = "Trimestre", y = "Precio del activo", color = "Movimiento") +
theme_light()
Dado que estamos analizando un caso extremo, nos fijamos en los precios que se encuentran en rojo, ya que representan el peor de los escenarios en los que el activo a medida que van transcurriendo los semestres empiezan a disminuir su cotización en el mercado. Su valor difiere a lo encontrado con el VaR por temas de probabilidad de ocurrencia en escenarios y diferencias en los supuestos de cada método.
El precio arranca en 226 y como máximo puede llega a 404 y en el caso adverso que nos interesa analizar llega a 126.
#Valor opcion PUT para LNG
valor_actual_put_LNG <- opcion_put_LNG[1,1]
option_df_put_LNG <- melt(opcion_put_LNG, varnames = c("T", "Nodo"), value.name = "Valor")
option_df_put_LNG <- subset(option_df_put_LNG, Valor > 0)
option_df_put_LNG <- option_df_put_LNG[order(option_df_put_LNG$T, option_df_put_LNG$Nodo), ]
option_df_put_LNG$Movimiento <- ifelse(option_df_put_LNG$Valor >= lag(option_df_put_LNG$Valor, default = option_df_put_LNG$Valor[1]), "Sube", "Baja")
ggplot(option_df_put_LNG, aes(x = T-1, y = Valor, color = Movimiento)) +
geom_point(size = 3) +
geom_text(aes(label = round(Valor, 2)), vjust = -0.5, size = 3) +
geom_line(aes(group = T), color = "gray") +
scale_color_manual(values = c("Sube" = "blue", "Baja" = "orange")) +
labs(title = "Árbol Binomial - Valor Opción PUT LNG", x = "Trimestre", y = "Valor de la opción", color = "Movimiento") +
theme_light()
En la gráfica se presentan los aumentos en el valor de las opciones debido a que como estamos en un Put, a medida de que el precio cae, el valor de la opción aumenta. Dado que se trata de una opción americana, se evalúa en cada nodo la posibilidad de ejercerla anticipadamente, lo cual explica los incrementos en valor frente a trayectorias bajistas. Esto resulta útil para cubrir caídas esperadas en el precio del activo, reduciendo así el riesgo del portafolio en escenarios adversos.
Es decir la Put gana valor conforme cae el precio del activo, y pierde valor cuando el precio sube.
La idea es tomar el portafolio con la inversión al 85% y compararlo con y sin cobertura para dimensionar el impacto que este tiene el uso de la opción put con opción americana.
costo_cobertura_LNG=cantidad_acciones_cobertura[1]*valor_actual_put_LNG
print(costo_cobertura_LNG)
## [1] 24862.15
perdida_sin_cobertura <- diff(valor_portafolio_trimestral_cobertura)
print(perdida_sin_cobertura)
## [,1]
## [1,] -163172.30
## [2,] -66815.15
## [3,] -105663.35
## [4,] -59158.43
## [5,] -55252.25
## [6,] -13441.27
## [7,] -72742.02
# Extraer el valor de la opción PUT en el nodo central para cada trimestre
valor_opcion_tramo_LNG <- numeric(7)
for (i in 1:7) {
fila <- i + 1 # fila central del árbol en el periodo t+1
columna <- i + 1 # columna del periodo t+1
valor_opcion_tramo_LNG[i] <- opcion_put_LNG[fila, columna] * cantidad_acciones_cobertura[1]
}
perdida_con_cobertura_tramo <- perdida_sin_cobertura + valor_opcion_tramo_LNG
# Crear los tramos
tramos <- paste0("T", 1:length(perdida_sin_cobertura))
# Crear el data frame
df_LNG <- data.frame(
Tramo = rep(tramos, 2),
Perdida = c(perdida_sin_cobertura, perdida_con_cobertura_tramo),
Tipo = rep(c("Sin cobertura", "Con cobertura"), each = length(perdida_sin_cobertura))
)
# Convertir factores para orden correcto
df_LNG$Tramo <- factor(df_LNG$Tramo, levels = tramos)
df_LNG$Tipo <- factor(df_LNG$Tipo, levels = c("Sin cobertura", "Con cobertura"))
# Crear gráfico de barras sin etiquetas
ggplot(df_LNG, aes(x = Tramo, y = Perdida, fill = Tipo)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.8), width = 0.7) +
scale_fill_manual(values = c("firebrick", "steelblue")) +
labs(title = "Comparación de pérdidas por tramo - LNG",
y = "Pérdida en USD",
x = "Tramo",
fill = "Tipo de Pérdida") +
theme_minimal(base_size = 13) +
theme(
legend.position = "right",
legend.title = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.x = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
sigma_META_trimestral <- sigma_META * sqrt(dt)
su_META <- exp(sigma_META_trimestral * sqrt(dt))
sd_META <- 1 / su_META
dividendo_META <- 0.02 / 4
psubir_META <- (exp((rf_continua_trimestral - dividendo_META) * dt) - sd_META) / (su_META - sd_META)
pbajar_META <- 1 - psubir_META
peso_META <- pesos_mediavarianza[2]
q_META <- (peso_META * inversion_cobertura) / S0_META
KPut_META <- 505
# Valuación de PUT (comprada)
opcion_put_META <- American_option(S0_META, KPut_META, periodos, dt, rf_continua_trimestral,
dividendo_META, su_META, sd_META, psubir_META, pbajar_META, "put")
tree_META <- stock_tree(S0_META, periodos, su_META, sd_META)
# Asegurar que es matriz numérica
tree_META <- as.matrix(tree_META)
print(tree_META)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 576.7400 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.000
## [2,] 516.8282 643.5969 0.0000 0.0000 0.0000 0.0000 0.0000 0.000
## [3,] 463.1400 576.7400 718.2040 0.0000 0.0000 0.0000 0.0000 0.000
## [4,] 415.0290 516.8282 643.5969 801.4597 0.0000 0.0000 0.0000 0.000
## [5,] 371.9158 463.1400 576.7400 718.2040 894.3666 0.0000 0.0000 0.000
## [6,] 333.2811 415.0290 516.8282 643.5969 801.4597 998.0434 0.0000 0.000
## [7,] 298.6598 371.9158 463.1400 576.7400 718.2040 894.3666 1113.7386 0.000
## [8,] 267.6350 333.2811 415.0290 516.8282 643.5969 801.4597 998.0434 1242.846
## [9,] 239.8331 298.6598 371.9158 463.1400 576.7400 718.2040 894.3666 1113.739
## [,9]
## [1,] 0.000
## [2,] 0.000
## [3,] 0.000
## [4,] 0.000
## [5,] 0.000
## [6,] 0.000
## [7,] 0.000
## [8,] 0.000
## [9,] 1386.919
# Convertir con melt
tree_df_META <- melt(tree_META, varnames = c("T", "Nodo"), value.name = "Precio")
# Quitar ceros o valores vacíos (nodos no existentes)
tree_df_META <- subset(tree_df_META, Precio > 0)
# Agregar una columna que identifique si el precio sube o baja respecto al nodo anterior
tree_df_META <- tree_df_META[order(tree_df_META$T, tree_df_META$Nodo), ]
tree_df_META$Movimiento <- ifelse(tree_df_META$Precio >= lag(tree_df_META$Precio, default = tree_df_META$Precio[1]), "Sube", "Baja")
# Gráfico del árbol binomial de precios
ggplot(tree_df_META, aes(x = T-1, y = Precio, color = Movimiento)) +
geom_point(size = 3) +
geom_text(aes(label = round(Precio, 2)), vjust = -0.5, size = 3) +
geom_line(aes(group = T), color = "gray") +
scale_color_manual(values = c("Sube" = "green3", "Baja" = "red3")) +
labs(title = "Árbol Binomial de Precios - META", x = "Trimestre", y = "Precio del activo", color = "Movimiento") +
theme_light()
El precio puede alcanzar hasta 1386.92 USD en el caso de subir constantemente y puede caer hasta 239.83 USD si tiene una trayectoria completamente a la baja.
# Valor opción PUT para META
valor_actual_put_META <- opcion_put_META[1,1]
option_df_put_META <- melt(opcion_put_META, varnames = c("T", "Nodo"), value.name = "Valor")
option_df_put_META <- subset(option_df_put_META, Valor > 0)
option_df_put_META <- option_df_put_META[order(option_df_put_META$T, option_df_put_META$Nodo), ]
option_df_put_META$Movimiento <- ifelse(option_df_put_META$Valor >= lag(option_df_put_META$Valor, default = option_df_put_META$Valor[1]), "Sube", "Baja")
ggplot(option_df_put_META, aes(x = T-1, y = Valor, color = Movimiento)) +
geom_point(size = 3) +
geom_text(aes(label = round(Valor, 2)), vjust = -0.5, size = 3) +
geom_line(aes(group = T), color = "gray") +
scale_color_manual(values = c("Sube" = "blue", "Baja" = "orange")) +
labs(title = "Árbol Binomial - Valor Opción PUT META", x = "Trimestre", y = "Valor de la opción", color = "Movimiento") +
theme_light()
El nodo en el trimestre 0 tiene un valor de 38.69, que es el precio actual de la Put sobre META. La opción alcanza hasta 265.17 en los escenarios más bajistas. Por otro lado, si META sube y el precio final está por encima del strike, la opción vale poco.
Al comparar este árbol con el del valor de la opción PUT, se evidencia cómo el valor de la cobertura crece conforme el activo pierde valor, cumpliendo su función como mecanismo de protección frente a escenarios adversos.
costo_cobertura_META = cantidad_acciones_cobertura[2] * valor_actual_put_META
print(costo_cobertura_META)
## [1] 20592.31
perdida_sin_cobertura <- diff(valor_portafolio_trimestral_cobertura)
print(perdida_sin_cobertura)
## [,1]
## [1,] -163172.30
## [2,] -66815.15
## [3,] -105663.35
## [4,] -59158.43
## [5,] -55252.25
## [6,] -13441.27
## [7,] -72742.02
# Extraer el valor de la opción PUT en el nodo central para cada trimestre
valor_opcion_tramo_META <- numeric(7)
for (i in 1:7) {
fila <- i + 1 # fila central del árbol en el periodo t+1
columna <- i + 1 # columna del periodo t+1
valor_opcion_tramo_META[i] <- opcion_put_META[fila, columna] * cantidad_acciones_cobertura[2]
}
perdida_con_cobertura_tramo <- perdida_sin_cobertura + valor_opcion_tramo_META
# Crear los tramos
tramos <- paste0("T", 1:length(perdida_sin_cobertura))
# Crear el data frame
df_META <- data.frame(
Tramo = rep(tramos, 2),
Perdida = c(perdida_sin_cobertura, perdida_con_cobertura_tramo),
Tipo = rep(c("Sin cobertura", "Con cobertura"), each = length(perdida_sin_cobertura))
)
# Convertir factores para orden correcto
df_META$Tramo <- factor(df_META$Tramo, levels = tramos)
df_META$Tipo <- factor(df_META$Tipo, levels = c("Sin cobertura", "Con cobertura"))
# Crear gráfico de barras sin etiquetas
ggplot(df_META, aes(x = Tramo, y = Perdida, fill = Tipo)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.8), width = 0.7) +
scale_fill_manual(values = c("firebrick", "steelblue")) +
labs(title = "Comparación de pérdidas por tramo - META",
y = "Pérdida en USD",
x = "Tramo",
fill = "Tipo de Pérdida") +
theme_minimal(base_size = 13) +
theme(
legend.position = "right",
legend.title = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.x = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
#NMIH
sigma_NMIH_trimestral <- sigma_NMIH * sqrt(dt)
su_NMIH <- exp(sigma_NMIH_trimestral * sqrt(dt))
sd_NMIH <- 1 / su_NMIH
dividendo_NMIH <- 0 # No tiene tasa de dividendos
psubir_NMIH <- (exp((rf_continua_trimestral - dividendo_NMIH) * dt) - sd_NMIH) / (su_NMIH - sd_NMIH)
pbajar_NMIH <- 1 - psubir_NMIH
peso_NMIH <- pesos_mediavarianza[3]
q_NMIH <- (peso_NMIH * inversion_cobertura) / S0_NMIH
KPut_NMIH <- 35
# Valuación de PUT (comprada)
opcion_put_NMIH <- European_option(S0_NMIH, KPut_NMIH, periodos, dt, rf_continua_trimestral,
dividendo_NMIH, su_NMIH, sd_NMIH, psubir_NMIH, pbajar_NMIH, "put")
tree_NMIH <- stock_tree(S0_NMIH, periodos, su_NMIH, sd_NMIH)
# Asegurar que es matriz numérica
tree_NMIH <- as.matrix(tree_NMIH)
print(tree_NMIH)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 36.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000
## [2,] 33.64985 38.51429 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000
## [3,] 31.45311 36.00000 41.20419 0.00000 0.00000 0.00000 0.00000 0.00000
## [4,] 29.39979 33.64985 38.51429 44.08195 0.00000 0.00000 0.00000 0.00000
## [5,] 27.48051 31.45311 36.00000 41.20419 47.16069 0.00000 0.00000 0.00000
## [6,] 25.68653 29.39979 33.64985 38.51429 44.08195 50.45447 0.00000 0.00000
## [7,] 24.00966 27.48051 31.45311 36.00000 41.20419 47.16069 53.97828 0.00000
## [8,] 22.44226 25.68653 29.39979 33.64985 38.51429 44.08195 50.45447 57.74820
## [9,] 20.97718 24.00966 27.48051 31.45311 36.00000 41.20419 47.16069 53.97828
## [,9]
## [1,] 0.00000
## [2,] 0.00000
## [3,] 0.00000
## [4,] 0.00000
## [5,] 0.00000
## [6,] 0.00000
## [7,] 0.00000
## [8,] 0.00000
## [9,] 61.78142
# Convertir con melt
tree_df_NMIH <- melt(tree_NMIH, varnames = c("T", "Nodo"), value.name = "Precio")
# Quitar ceros o valores vacíos (nodos no existentes)
tree_df_NMIH <- subset(tree_df_NMIH, Precio > 0)
# Agregar una columna que identifique si el precio sube o baja respecto al nodo anterior
tree_df_NMIH <- tree_df_NMIH[order(tree_df_NMIH$T, tree_df_NMIH$Nodo), ]
tree_df_NMIH$Movimiento <- ifelse(tree_df_NMIH$Precio >= lag(tree_df_NMIH$Precio, default = tree_df_NMIH$Precio[1]), "Sube", "Baja")
# Gráfico del árbol binomial de precios
ggplot(tree_df_NMIH, aes(x = T-1, y = Precio, color = Movimiento)) +
geom_point(size = 3) +
geom_text(aes(label = round(Precio, 2)), vjust = -0.5, size = 3) +
geom_line(aes(group = T), color = "gray") +
scale_color_manual(values = c("Sube" = "green3", "Baja" = "red3")) +
labs(title = "Árbol Binomial de Precios - NMIH", x = "Trimestre", y = "Precio del activo", color = "Movimiento") +
theme_light()
A diferencia de activos como LNG o META, los movimientos en NMIH son más moderados, con un rango de precios más acotado. Este comportamiento sugiere que la opción Put podría no generar valores extremos, aunque sí es útil en escenarios bajistas.
Al ser una opción europea, su capacidad de protección está limitada a eventos extremos al vencimiento. Aun así, en casos donde el precio cae por debajo de 30, comienza a generar valor que puede compensar las pérdidas en el portafolio.
# Valor opción PUT para NMIH
valor_actual_put_NMIH <- opcion_put_NMIH[1,1]
option_df_put_NMIH <- melt(opcion_put_NMIH, varnames = c("T", "Nodo"), value.name = "Valor")
option_df_put_NMIH <- subset(option_df_put_NMIH, Valor > 0)
option_df_put_NMIH <- option_df_put_NMIH[order(option_df_put_NMIH$T, option_df_put_NMIH$Nodo), ]
option_df_put_NMIH$Movimiento <- ifelse(option_df_put_NMIH$Valor >= lag(option_df_put_NMIH$Valor, default = option_df_put_NMIH$Valor[1]), "Sube", "Baja")
ggplot(option_df_put_NMIH, aes(x = T-1, y = Valor, color = Movimiento)) +
geom_point(size = 3) +
geom_text(aes(label = round(Valor, 2)), vjust = -0.5, size = 3) +
geom_line(aes(group = T), color = "gray") +
scale_color_manual(values = c("Sube" = "blue", "Baja" = "orange")) +
labs(title = "Árbol Binomial - Valor Opción PUT NMIH", x = "Trimestre", y = "Valor de la opción", color = "Movimiento") +
theme_light()
A medida que avanza el tiempo y la acción cae, el valor de la opción crece, En escenarios donde el precio cae a aproximadamente 21, la PUT vale hasta 14.02 USD al vencimiento. Por otro lado, en escenarios donde el precio sube, precio > 36, el valor converge hacia 0.
costo_cobertura_NMIH = cantidad_acciones_cobertura[3] * valor_actual_put_NMIH
print(costo_cobertura_NMIH)
## [1] 20067.58
perdida_sin_cobertura <- diff(valor_portafolio_trimestral_cobertura)
print(perdida_sin_cobertura)
## [,1]
## [1,] -163172.30
## [2,] -66815.15
## [3,] -105663.35
## [4,] -59158.43
## [5,] -55252.25
## [6,] -13441.27
## [7,] -72742.02
# Extraer el valor de la opción PUT en el nodo central para cada trimestre
valor_opcion_tramo_NMIH <- numeric(7)
for (i in 1:7) {
fila <- i + 1 # fila central del árbol en el periodo t+1
columna <- i + 1 # columna del periodo t+1
valor_opcion_tramo_NMIH[i] <- opcion_put_NMIH[fila, columna] * cantidad_acciones_cobertura[3]
}
perdida_con_cobertura_tramo <- perdida_sin_cobertura + valor_opcion_tramo_NMIH
# Crear los tramos
tramos <- paste0("T", 1:length(perdida_sin_cobertura))
# Crear el data frame
df_NMIH <- data.frame(
Tramo = rep(tramos, 2),
Perdida = c(perdida_sin_cobertura, perdida_con_cobertura_tramo),
Tipo = rep(c("Sin cobertura", "Con cobertura"), each = length(perdida_sin_cobertura))
)
# Convertir factores para orden correcto
df_NMIH$Tramo <- factor(df_NMIH$Tramo, levels = tramos)
df_NMIH$Tipo <- factor(df_NMIH$Tipo, levels = c("Sin cobertura", "Con cobertura"))
# Crear gráfico de barras sin etiquetas
ggplot(df_NMIH, aes(x = Tramo, y = Perdida, fill = Tipo)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.8), width = 0.7) +
scale_fill_manual(values = c("firebrick", "steelblue")) +
labs(title = "Comparación de pérdidas por tramo - NMIH",
y = "Pérdida en USD",
x = "Tramo",
fill = "Tipo de Pérdida") +
theme_minimal(base_size = 13) +
theme(
legend.position = "right",
legend.title = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.x = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Hasta ahora no habíamos interpretado el tema de la cobertura lograda por la opción Put americana y europea. Graficamente, hay un impacto en la reducción de las perdidas generadas por la caída prolongada del rendimiento del portafolio en este contexto adverso. Sin embargo es adecuado compararlo con su costo, para evaluar si vale la pena obtenerla.
#LNG
print(costo_cobertura_LNG)
## [1] 24862.15
print(sum(valor_opcion_tramo_LNG))
## [1] 29466.09
#META
print(costo_cobertura_META)
## [1] 20592.31
print(sum(valor_opcion_tramo_META))
## [1] 15708.32
#NMIH
print(costo_cobertura_NMIH)
## [1] 20067.58
print(sum(valor_opcion_tramo_NMIH))
## [1] 16708.01
De esto podemos concluir que:
La opción americana ejercida en la acción LNG si fue efectiva ya que, a pesar de que cubrirse tiene un costo, el cubrimiento fue mayor, haciendo que se compense de cierta forma las pérdidas
La opción americana ejercida en la acción META, no fue efectiva, ya que el costo fue mayor. Esto quiere decir que se está pagando más por la opción que por lo que se recupera con ella y a su vez puede interpretarse como que la acción no tuvo un escenario adverso con tanta severidad lo cual hizo que la Put no generara suficiente valor. Esto tendría mucho sentido, porque a pesar de que META tiene una alta volatilidad, es una acción que se incluyó en el portafolio para que compensara el rendimiento que tienen las otras 2, entonces era un efecto esperado. Sin embargo, se puede evaluar si hacer uso de una opción europea, ya que es menos costosa a comparación de una opción americana
La opción europea ejercida en la acción NMIH, tampoco fue efectiva, ya que el costo fue mayor. Esto al analizar el valor opción Put graficamente podía haberse esperado ya que sus movimientos entre la valoración son más moderados a comparación de las otras 2 acciones, por lo que se puede inferir que no se presentó un caso con la severidad necesaria para que la Put obtuviera mayor valor. En este caso, se usó la opción más asequible que es la europea, por lo tanto, este activo no tendría necesidad de usar opciones para cubrirse, ya que incurre en un costo más alto
Hay que recordar que la cobertura se hizo al 85%, por lo que había un 15% que estaba asumiendo todos los riesgos del mercado, por lo tanto esta proporción debe de ser agregada a portafolio final.
inversion_sin_cobertura<-inversion*0.15
cantidad_acciones_sin_cobertura<-(inversion_sin_cobertura*pesos_mediavarianza)/precios_iniciales
valor_portafolio_trimestral_sin_cobertura <- precios_matriz %*% cantidad_acciones_sin_cobertura
print(valor_portafolio_trimestral_sin_cobertura)
## [,1]
## [1,] 150000.00
## [2,] 121204.89
## [3,] 109413.98
## [4,] 90767.50
## [5,] 80327.78
## [6,] 70577.39
## [7,] 68205.40
## [8,] 55368.57
Si al 85% restante no se hubiera aplicado alguna estrategia de cobertura con opciones:
print(valor_portafolio_trimestral_sin_cobertura+valor_portafolio_trimestral_cobertura)
## [,1]
## [1,] 1000000.0
## [2,] 808032.6
## [3,] 729426.5
## [4,] 605116.7
## [5,] 535518.5
## [6,] 470515.9
## [7,] 454702.6
## [8,] 369123.8
Cobertura obtenida por las opciones put para compensar caidas de precio:
cobertura_total<-valor_opcion_tramo_LNG+valor_opcion_tramo_META+valor_opcion_tramo_NMIH
print(cobertura_total)
## [1] 37614.2743 17569.6267 5785.7012 912.8146 0.0000 0.0000 0.0000
print(valor_portafolio_trimestral)
## [,1]
## [1,] 1000000.0
## [2,] 808032.6
## [3,] 729426.5
## [4,] 605116.7
## [5,] 535518.5
## [6,] 470515.9
## [7,] 454702.6
## [8,] 369123.8
Ahora, ignorando el hecho de que en las opciones de META y NMIH se incurrió en un mayor costo por adquirir la opción que por el cubrimiento de la misma. Es importante analizar como la opción ayudó de cierta manera a compensar las pérdidas. Es adecuado mencionar, que las opciones no llegaron al punto tal de compensar totalmente una pérdida, pero disminuyó un poco su impacto.
# Crear un nuevo objeto con los valores originales
valor_portafolio_con_cobertura <- valor_portafolio_trimestral
# Sumar cobertura_total a los tramos
valor_portafolio_con_cobertura[2:8, 1] <- valor_portafolio_trimestral[2:8, 1] + cobertura_total
print(valor_portafolio_con_cobertura)
## [,1]
## [1,] 1000000.0
## [2,] 845646.9
## [3,] 746996.2
## [4,] 610902.4
## [5,] 536431.4
## [6,] 470515.9
## [7,] 454702.6
## [8,] 369123.8
De forma gráfica se ve así:
# Crear vector con los valores sin cobertura
portafolio_sin_cobertura <- as.vector(valor_portafolio_trimestral)
# Crear vector con los valores con cobertura
portafolio_con_cobertura <- as.vector(valor_portafolio_con_cobertura)
# Crear etiquetas de tramos
tramos <- paste0("T", 1:8)
# Construir el data frame para graficar
df_portafolio <- data.frame(
Tramo = rep(tramos, 2),
Valor = c(portafolio_sin_cobertura, portafolio_con_cobertura),
Tipo = rep(c("Sin cobertura", "Con cobertura"), each = 8)
)
# Asegurar el orden correcto de los factores
df_portafolio$Tramo <- factor(df_portafolio$Tramo, levels = tramos)
df_portafolio$Tipo <- factor(df_portafolio$Tipo, levels = c("Sin cobertura", "Con cobertura"))
# Crear el gráfico
ggplot(df_portafolio, aes(x = Tramo, y = Valor, fill = Tipo)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.8), width = 0.7) +
scale_fill_manual(values = c("firebrick", "steelblue")) +
labs(title = "Evolución del valor del portafolio con y sin cobertura",
y = "Valor del Portafolio (USD)",
x = "Trimestre",
fill = "Escenario") +
theme_minimal(base_size = 13) +
theme(
legend.position = "right",
legend.title = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.x = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Hay algo importante que interpretar y es el por qué no se obtiene compensación en los últimos trimestres pero en los primeros si. Ya que se podría llegar a pensar que si el Put es más alto cuando el precio baja, por qué no obtenemos compensación.
Técnicamente, el portafolio ya perdió mucho valor en los trimestres anteriores. Por lo que la perdida adicional en los trimestres anteriores es más pequeña. A pesar de que la Put tiene un valor alto, el portafolio ya ha absorbido la mayor parte de las pérdidas previamente.
Por lo tanto se concluye que, en escenarios de caída sostenida, la cobertura es más efectiva al inicio del periodo, cuando las perdidas son más significativas. Pero, en el final, la cobertura sigue teniendo valor teórico, pero su valor práctico como escudo frente a nuevas caídas se reduce considerablemente.