## 1. Carga de paquetes
# Instalar y cargar paquetes necesarios
required_packages <- c("quantmod", "tidyquant", "PortfolioAnalytics",
"PerformanceAnalytics", "xts", "ggplot2",
"plotly", "reshape2", "scales", "zoo",
"timetk", "tidyverse", "fGarch")
# Instalar y cargar paquetes faltantes
for (pkg in required_packages) {
if (!require(pkg, character.only = TRUE)) {
install.packages(pkg, repos = "https://cran.rstudio.com/")
library(pkg, character.only = TRUE)
}
}
## 2. Configuración inicial y obtención de datos
# Definir las fechas y acciones
inicio <- as.Date("2022-06-22")
fin <- as.Date("2025-03-31")
hoy <- Sys.Date()
tickers <- c('APH', 'MU', 'NVDA')
# Inversión inicial
investment <- 1000000 # $1 millón
# Tasa libre de riesgo (Tbond a 3 meses)
rf_rate <- 0.05 # Aproximadamente 5% anual
# Obtener datos históricos de precios
price_data <- tq_get(tickers,
from = inicio,
to = hoy,
get = 'stock.prices')
# Calcular los retornos logarítmicos diarios
log_ret_tidy <- price_data %>%
group_by(symbol) %>%
tq_transmute(select = adjusted,
mutate_fun = periodReturn,
period = 'daily',
col_rename = 'ret',
type = 'log')
# Convertir a formato amplio y a xts
log_ret_xts <- log_ret_tidy %>%
spread(symbol, value = ret) %>%
tk_xts()
# Calcular retornos medios diarios
mean_ret <- colMeans(log_ret_xts, na.rm = TRUE)
print("Retornos medios diarios:")
## [1] "Retornos medios diarios:"
print(round(mean_ret, 5))
## APH MU NVDA
## 0.00093 0.00022 0.00250
# Calcular matriz de covarianza (anualizada)
cov_mat <- cov(log_ret_xts, use = "complete.obs") * 252
print("Matriz de covarianza anualizada:")
## [1] "Matriz de covarianza anualizada:"
print(round(cov_mat, 4))
## APH MU NVDA
## APH 0.0749 0.0748 0.0897
## MU 0.0748 0.2291 0.1508
## NVDA 0.0897 0.1508 0.2869
## 3. Construcción del portafolio de media-varianza
# Generar 5000 portafolios aleatorios
set.seed(123)
num_port <- 5000
all_wts <- matrix(nrow = num_port, ncol = length(tickers))
port_returns <- vector('numeric', length = num_port)
port_risk <- vector('numeric', length = num_port)
sharpe_ratio <- vector('numeric', length = num_port)
# Crear portafolios aleatorios
for (i in seq_along(port_returns)) {
wts <- runif(length(tickers))
wts <- wts/sum(wts)
# Almacenar pesos
all_wts[i,] <- wts
# Retornos del portafolio
port_ret <- sum(wts * mean_ret)
port_ret <- ((port_ret + 1)^252) - 1
port_returns[i] <- port_ret
# Riesgo del portafolio
port_sd <- sqrt(t(wts) %*% (cov_mat %*% wts))
port_risk[i] <- port_sd
# Sharpe ratio
sr <- (port_ret - rf_rate) / port_sd
sharpe_ratio[i] <- sr
}
# Crear tabla con los valores del portafolio
portfolio_values <- tibble(Return = port_returns,
Risk = port_risk,
SharpeRatio = sharpe_ratio)
# Convertir matriz a tibble y cambiar nombres de columnas
all_wts <- as_tibble(all_wts)
colnames(all_wts) <- colnames(log_ret_xts)
# Combinar todos los valores
portfolio_values <- bind_cols(all_wts, portfolio_values)
# Encontrar el portafolio con mínima varianza y máximo Sharpe ratio
min_var <- portfolio_values[which.min(portfolio_values$Risk),]
max_sr <- portfolio_values[which.max(portfolio_values$SharpeRatio),]
# Usar los pesos del portafolio óptimo (máximo Sharpe ratio)
optimal_weights <- as.numeric(max_sr[1:length(tickers)])
names(optimal_weights) <- tickers
print("Pesos óptimos del portafolio (Máximo Sharpe Ratio):")
## [1] "Pesos óptimos del portafolio (Máximo Sharpe Ratio):"
print(round(optimal_weights, 4))
## APH MU NVDA
## 0.0605 0.0058 0.9336
# Inversión por activo
investment_per_asset <- optimal_weights * investment
print("Inversión por activo:")
## [1] "Inversión por activo:"
print(round(investment_per_asset, 2))
## APH MU NVDA
## 60536.15 5828.19 933635.66
# Determinar la acción con menor peso
min_weight_ticker <- names(which.min(optimal_weights))
print(paste("La acción con menor peso en el portafolio es:", min_weight_ticker))
## [1] "La acción con menor peso en el portafolio es: MU"
### 3.1 Frontera eficiente
# Crear un dataframe con los portafolios generados
frontier_data <- data.frame(
Return = port_returns,
Risk = port_risk
)
# Añadir puntos especiales
frontier_data$Type <- "Regular"
frontier_data$Type[which.min(port_risk)] <- "Mínima Varianza"
frontier_data$Type[which.max(sharpe_ratio)] <- "Máximo Sharpe"
# Crear gráfico de frontera eficiente
frontier_plot <- ggplot(frontier_data, aes(x = Risk, y = Return, color = Type)) +
geom_point(size = 1, alpha = 0.5) +
geom_point(data = subset(frontier_data, Type != "Regular"),
aes(x = Risk, y = Return),
size = 4) +
scale_color_manual(values = c("Mínima Varianza" = "red",
"Máximo Sharpe" = "green",
"Regular" = "blue")) +
labs(title = "Frontera Eficiente",
x = "Riesgo (Volatilidad)",
y = "Rendimiento Esperado",
color = "Tipo de Portafolio") +
theme_minimal() +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(labels = scales::percent)
print(frontier_plot)
### 3.2 Ponderaciones óptimas
# Gráfico de pesos óptimos
weights_df <- data.frame(
Ticker = names(optimal_weights),
Weight = optimal_weights
)
weights_plot <- ggplot(weights_df, aes(x = Ticker, y = Weight, fill = Ticker)) +
geom_bar(stat = "identity") +
geom_text(aes(label = scales::percent(Weight, accuracy = 0.1)),
vjust = -0.5) +
labs(title = "Ponderaciones Óptimas del Portafolio",
x = "Acción",
y = "Peso") +
theme_minimal() +
scale_y_continuous(labels = scales::percent)
print(weights_plot)
## 4. Simulación de precios y análisis de rendimiento
# Obtener precios actuales
last_prices <- price_data %>%
group_by(symbol) %>%
slice_tail(n = 1) %>%
select(symbol, adjusted)
# Convertir a vector
current_prices <- last_prices$adjusted
names(current_prices) <- last_prices$symbol
# Calcular volatilidades históricas (anualizadas)
volatility <- apply(log_ret_xts, 2, sd, na.rm = TRUE) * sqrt(252)
print("Volatilidades anualizadas:")
## [1] "Volatilidades anualizadas:"
print(round(volatility, 4))
## APH MU NVDA
## 0.2737 0.4787 0.5356
# Función para simular precios con Movimiento Browniano Geométrico
simulate_prices <- function(S0, mu, sigma, T, dt, n_sim) {
# Asegurar que T sea positivo
if (T <= 0) T <- 1
# Calcular número de pasos y asegurar que sea al menos 1
n_steps <- max(1, ceiling(T/dt))
# Crear matriz para almacenar resultados
S <- matrix(0, nrow = n_steps + 1, ncol = n_sim)
S[1, ] <- S0
# Simular precios
for (i in 2:(n_steps + 1)) {
Z <- rnorm(n_sim)
S[i, ] <- S[i-1, ] * exp((mu - 0.5 * sigma^2) * dt + sigma * sqrt(dt) * Z)
}
return(S)
}
# Periodo de simulación en años
T <- as.numeric(difftime(fin, hoy, units = "days")) / 365
# Asegurar que T sea positivo (importante para la simulación)
if (T <= 0) {
cat("Advertencia: La fecha final es anterior o igual a la fecha actual. Usando 1 año como horizonte de simulación.\n")
T <- 1
}
## Advertencia: La fecha final es anterior o igual a la fecha actual. Usando 1 año como horizonte de simulación.
# Parámetros de simulación
n_simulations <- 1000
dt <- 1/252 # Paso diario (252 días de trading por año)
# Simular precios para cada activo
simulated_prices <- list()
for (i in 1:length(tickers)) {
ticker <- tickers[i]
S0 <- current_prices[ticker]
mu <- mean_ret[ticker] * 252 # Retorno anualizado
sig <- volatility[ticker]
simulated_prices[[ticker]] <- simulate_prices(
S0 = S0,
mu = mu,
sigma = sig,
T = T,
dt = dt,
n_sim = n_simulations
)
}
# Calcular valor del portafolio simulado
n_steps <- nrow(simulated_prices[[1]])
portfolio_values_sim <- matrix(0, nrow = n_steps, ncol = n_simulations)
for (j in 1:n_simulations) {
for (i in 1:n_steps) {
portfolio_value <- 0
for (k in 1:length(tickers)) {
ticker <- tickers[k]
portfolio_value <- portfolio_value +
investment_per_asset[k] * simulated_prices[[ticker]][i, j] / current_prices[ticker]
}
portfolio_values_sim[i, j] <- portfolio_value
}
}
# Calcular estadísticas del portafolio
# Retornos esperados
expected_return <- mean(portfolio_values_sim[n_steps, ]) / investment - 1
print(paste("Rendimiento esperado del portafolio:", round(expected_return * 100, 2), "%"))
## [1] "Rendimiento esperado del portafolio: 84.15 %"
# Volatilidad del portafolio
portfolio_volatility <- sd(portfolio_values_sim[n_steps, ]) / investment
print(paste("Volatilidad del portafolio:", round(portfolio_volatility * 100, 2), "%"))
## [1] "Volatilidad del portafolio: 104.14 %"
# VaR al 1% y 5%
VaR_1pct <- quantile(portfolio_values_sim[n_steps, ] - investment, 0.01)
VaR_5pct <- quantile(portfolio_values_sim[n_steps, ] - investment, 0.05)
print(paste("VaR al 1%:", round(VaR_1pct, 2)))
## [1] "VaR al 1%: -464822.35"
print(paste("VaR al 5%:", round(VaR_5pct, 2)))
## [1] "VaR al 5%: -321940.66"
# Índice de Sharpe
sharpe <- (expected_return - rf_rate) / portfolio_volatility
print(paste("Índice de Sharpe del portafolio:", round(sharpe, 4)))
## [1] "Índice de Sharpe del portafolio: 0.76"
### 4.1 Trayectorias de simulación
# Extraemos solo algunas simulaciones para claridad
sample_indices <- sample(1:n_simulations, 50)
portfolio_data <- data.frame()
for (i in sample_indices) {
temp_df <- data.frame(
Tiempo = 1:nrow(portfolio_values_sim),
Valor = portfolio_values_sim[, i],
Simulacion = as.factor(i)
)
portfolio_data <- rbind(portfolio_data, temp_df)
}
# Añadir el valor promedio
avg_values <- rowMeans(portfolio_values_sim)
avg_df <- data.frame(
Tiempo = 1:length(avg_values),
Valor = avg_values,
Simulacion = "Promedio"
)
portfolio_data <- rbind(portfolio_data, avg_df)
# Gráfico de simulación
portfolio_plot <- ggplot(portfolio_data, aes(x = Tiempo, y = Valor, group = Simulacion)) +
geom_line(alpha = 0.2) +
geom_line(data = subset(portfolio_data, Simulacion == "Promedio"),
aes(x = Tiempo, y = Valor),
color = "red", size = 1) +
geom_hline(yintercept = investment, linetype = "dashed", color = "green") +
labs(
title = "Simulación del Valor del Portafolio",
x = "Tiempo (días)",
y = "Valor del Portafolio ($)"
) +
theme_minimal() +
scale_y_continuous(labels = scales::dollar)
print(portfolio_plot)
### 4.2 Distribución de valores finales
# Distribución de valores finales
final_values <- portfolio_values_sim[nrow(portfolio_values_sim), ]
values_df <- data.frame(Valor = final_values)
dist_plot <- ggplot(values_df, aes(x = Valor)) +
geom_histogram(bins = 30, fill = "skyblue", color = "white") +
geom_vline(xintercept = investment, color = "green", linetype = "dashed") +
geom_vline(xintercept = investment + VaR_1pct, color = "red", linetype = "dashed") +
geom_vline(xintercept = investment + VaR_5pct, color = "orange", linetype = "dashed") +
annotate("text", x = investment, y = max(hist(final_values, plot = FALSE)$counts) * 0.9,
label = "Inversión Inicial", color = "green", hjust = -0.1) +
annotate("text", x = investment + VaR_1pct, y = max(hist(final_values, plot = FALSE)$counts) * 0.8,
label = "VaR 1%", color = "red", hjust = -0.1) +
annotate("text", x = investment + VaR_5pct, y = max(hist(final_values, plot = FALSE)$counts) * 0.7,
label = "VaR 5%", color = "orange", hjust = -0.1) +
labs(
title = "Distribución de Valores Finales del Portafolio",
x = "Valor Final ($)",
y = "Frecuencia"
) +
scale_x_continuous(labels = scales::dollar) +
theme_minimal()
print(dist_plot)
## 5. Análisis de tendencia para identificar acciones en caída
# Analizar tendencia de las acciones para identificar las que están en caída
analizar_tendencia <- function(price_data, tickers, periodo=30) {
tendencias <- numeric(length(tickers))
names(tendencias) <- tickers
for (i in 1:length(tickers)) {
ticker <- tickers[i]
# Filtrar datos del ticker específico
ticker_data <- price_data[price_data$symbol == ticker, ]
# Asegurarse de que hay suficientes datos
if (nrow(ticker_data) >= periodo) {
# Obtener los últimos precios disponibles
ultimos_precios <- tail(ticker_data$adjusted, periodo)
# Calcular el retorno en el periodo
retorno_periodo <- (ultimos_precios[length(ultimos_precios)] / ultimos_precios[1]) - 1
tendencias[i] <- retorno_periodo
} else {
# Si no hay suficientes datos, asignar un valor neutral
tendencias[i] <- 0
warning(paste("No hay suficientes datos para calcular la tendencia de", ticker))
}
}
return(tendencias)
}
# Calcular tendencias
tendencias <- tryCatch({
analizar_tendencia(price_data, tickers)
}, error = function(e) {
# Si hay error, usamos la acción con menor peso como alternativa
cat("Error al calcular tendencias:", e$message, "\n")
cat("Usando la acción con menor peso como alternativa.\n")
temp <- numeric(length(tickers))
names(temp) <- tickers
temp[min_weight_ticker] <- -0.1 # Simulamos una caída
return(temp)
})
print("Tendencias recientes de las acciones:")
## [1] "Tendencias recientes de las acciones:"
print(round(tendencias * 100, 2))
## APH MU NVDA
## -11.75 -32.08 -27.60
# Identificar la acción con mayor caída
accion_caida <- names(which.min(tendencias))
print(paste("La acción con mayor caída es:", accion_caida))
## [1] "La acción con mayor caída es: MU"
## 6. Modelo binomial para valoración de opciones
# Implementación del modelo binomial
binomial_tree <- function(S, K, r, q, sigma, T, n, type, american = TRUE) {
dt <- T/n
u <- exp(sigma * sqrt(dt))
d <- 1/u
p <- (exp((r - q) * dt) - d) / (u - d) # Probabilidad neutral al riesgo
# Crear matriz de precios
prices <- matrix(0, n + 1, n + 1)
for (i in 0:n) {
for (j in 0:i) {
prices[j + 1, i + 1] <- S * u^(i - j) * d^j
}
}
# Crear matriz para valores de la opción
option_values <- matrix(0, n + 1, n + 1)
# Calcular payoff al vencimiento
if (type == "call") {
option_values[, n + 1] <- pmax(prices[, n + 1] - K, 0)
} else { # put
option_values[, n + 1] <- pmax(K - prices[, n + 1], 0)
}
# Retroceder en el árbol
for (j in seq(n, 1, -1)) {
for (i in 1:j) {
# Valor si mantenemos la opción
hold_value <- exp(-r * dt) * (p * option_values[i, j + 1] + (1 - p) * option_values[i + 1, j + 1])
# Valor si ejercemos la opción (solo para americana)
if (american) {
if (type == "call") {
exercise_value <- pmax(prices[i, j] - K, 0)
} else { # put
exercise_value <- pmax(K - prices[i, j], 0)
}
# Elegimos el mayor valor
option_values[i, j] <- max(hold_value, exercise_value)
} else {
option_values[i, j] <- hold_value
}
}
}
# Creamos un dataframe para visualización
tree_data <- data.frame()
for (i in 1:(n+1)) {
for (j in 1:i) {
if (!is.na(prices[j, i]) && !is.na(option_values[j, i])) {
tree_data <- rbind(tree_data, data.frame(
step = i - 1,
node = j - 1,
price = prices[j, i],
option_value = option_values[j, i]
))
}
}
}
return(list(
option_price = option_values[1, 1],
tree = tree_data,
prices = prices,
values = option_values
))
}
# Parámetros para valoración de opciones
# Usamos la acción con mayor caída
S0 <- current_prices[accion_caida] # Precio spot actual
K <- S0 # Strike price (ATM)
r <- rf_rate # Tasa libre de riesgo
q <- 0.01 # Tasa de dividendos (ajustar según la acción)
vol <- volatility[accion_caida] # Volatilidad anualizada
# Crear árboles binomiales con menos pasos para visualización
vis_steps <- 8 # Usar 8 pasos para representar los trimestres en 2 años
# Tiempo hasta vencimiento (2 años)
T_expiry <- 2
# Crear árboles binomiales solo para opción put (ya que estamos en un mercado bajista)
euro_put <- binomial_tree(S0, K, r, q, vol, T_expiry, vis_steps, "put", american = FALSE)
amer_put <- binomial_tree(S0, K, r, q, vol, T_expiry, vis_steps, "put", american = TRUE)
# Precios de las opciones put
option_prices <- data.frame(
Tipo = c("Put Europea", "Put Americana"),
Precio = c(euro_put$option_price, amer_put$option_price)
)
print("Precios de opciones put para la acción en caída:")
## [1] "Precios de opciones put para la acción en caída:"
print(option_prices)
## Tipo Precio
## 1 Put Europea 13.33513
## 2 Put Americana 14.29094
### 6.1 Visualización de árboles binomiales
# Función para visualizar el árbol binomial
visualize_binomial_tree <- function(tree_result, title) {
# Extraer los datos del árbol
prices <- tree_result$prices
values <- tree_result$values
n <- nrow(prices) - 1 # Número de pasos
# Crear dataframe para graficar
nodes <- data.frame()
for (i in 0:n) {
for (j in 0:i) {
if (i <= n && j <= i) {
nodes <- rbind(nodes, data.frame(
step = i,
node = j,
price = prices[j+1, i+1],
option_value = values[j+1, i+1]
))
}
}
}
# Crear gráfico básico
p <- ggplot(nodes, aes(x = step, y = node)) +
geom_point(aes(size = option_value, color = price), alpha = 0.8) +
scale_color_gradient(low = "blue", high = "red") +
ggtitle(title) +
xlab("Paso (Trimestre)") +
ylab("Nodo") +
theme_minimal()
# Añadir etiquetas de precios
for (i in 1:nrow(nodes)) {
p <- p + annotate(
"text",
x = nodes$step[i],
y = nodes$node[i] + 0.2,
label = sprintf("%.1f", nodes$price[i]),
size = 3
)
}
# Añadir etiquetas de valores de opción
for (i in 1:nrow(nodes)) {
p <- p + annotate(
"text",
x = nodes$step[i],
y = nodes$node[i] - 0.2,
label = sprintf("(%.1f)", nodes$option_value[i]),
size = 3,
color = "darkred"
)
}
return(p)
}
print_binomial_tree <- function(tree_result, title) {
# Extraer precios y valores
prices <- tree_result$prices
values <- tree_result$values
cat("\n", title, "\n\n")
cat("Árbol de precios:\n")
print(round(prices, 2))
cat("\nÁrbol de valores de opción:\n")
print(round(values, 2))
cat("\nPrecio de la opción:", round(tree_result$option_price, 4), "\n\n")
}
# Usar así:
print_binomial_tree(euro_put, paste("Opción Put Europea para", accion_caida))
##
## Opción Put Europea para MU
##
## Árbol de precios:
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
## [1,] 64.72 82.22 104.45 132.69 168.57 214.15 272.06 345.62 439.08
## [2,] 0.00 50.94 64.72 82.22 104.45 132.69 168.57 214.15 272.06
## [3,] 0.00 0.00 40.10 50.94 64.72 82.22 104.45 132.69 168.57
## [4,] 0.00 0.00 0.00 31.57 40.10 50.94 64.72 82.22 104.45
## [5,] 0.00 0.00 0.00 0.00 24.85 31.57 40.10 50.94 64.72
## [6,] 0.00 0.00 0.00 0.00 0.00 19.56 24.85 31.57 40.10
## [7,] 0.00 0.00 0.00 0.00 0.00 0.00 15.40 19.56 24.85
## [8,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 12.12 15.40
## [9,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 9.54
##
## Árbol de valores de opción:
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
## [1,] 13.34 8.05 3.77 1.05 0.00 0.00 0.00 0.00 0.00
## [2,] 0.00 18.17 11.91 6.19 1.97 0.00 0.00 0.00 0.00
## [3,] 0.00 0.00 23.95 17.07 9.95 3.71 0.00 0.00 0.00
## [4,] 0.00 0.00 0.00 30.40 23.57 15.53 6.97 0.00 0.00
## [5,] 0.00 0.00 0.00 0.00 36.96 31.01 23.22 13.10 0.00
## [6,] 0.00 0.00 0.00 0.00 0.00 42.92 38.40 32.43 24.62
## [7,] 0.00 0.00 0.00 0.00 0.00 0.00 47.80 44.41 39.87
## [8,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 51.83 49.32
## [9,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 55.18
##
## Precio de la opción: 13.3351
print_binomial_tree(amer_put, paste("Opción Put Americana para", accion_caida))
##
## Opción Put Americana para MU
##
## Árbol de precios:
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
## [1,] 64.72 82.22 104.45 132.69 168.57 214.15 272.06 345.62 439.08
## [2,] 0.00 50.94 64.72 82.22 104.45 132.69 168.57 214.15 272.06
## [3,] 0.00 0.00 40.10 50.94 64.72 82.22 104.45 132.69 168.57
## [4,] 0.00 0.00 0.00 31.57 40.10 50.94 64.72 82.22 104.45
## [5,] 0.00 0.00 0.00 0.00 24.85 31.57 40.10 50.94 64.72
## [6,] 0.00 0.00 0.00 0.00 0.00 19.56 24.85 31.57 40.10
## [7,] 0.00 0.00 0.00 0.00 0.00 0.00 15.40 19.56 24.85
## [8,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 12.12 15.40
## [9,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 9.54
##
## Árbol de valores de opción:
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
## [1,] 14.29 8.54 3.98 1.10 0.00 0.00 0.00 0.00 0.00
## [2,] 0.00 19.55 12.64 6.54 2.07 0.00 0.00 0.00 0.00
## [3,] 0.00 0.00 25.91 18.16 10.52 3.90 0.00 0.00 0.00
## [4,] 0.00 0.00 0.00 33.15 25.13 16.44 7.33 0.00 0.00
## [5,] 0.00 0.00 0.00 0.00 39.87 33.15 24.62 13.78 0.00
## [6,] 0.00 0.00 0.00 0.00 0.00 45.16 39.87 33.15 24.62
## [7,] 0.00 0.00 0.00 0.00 0.00 0.00 49.32 45.16 39.87
## [8,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 52.60 49.32
## [9,] 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 55.18
##
## Precio de la opción: 14.2909
# Datos para la opción put Europea de MU
precio_euro_put_MU <- matrix(
c(64.72, 82.22, 104.45, 132.69, 168.57, 214.15, 272.06, 345.62, 439.08,
0.00, 50.94, 64.72, 82.22, 104.45, 132.69, 168.57, 214.15, 272.06,
0.00, 0.00, 40.10, 50.94, 64.72, 82.22, 104.45, 132.69, 168.57,
0.00, 0.00, 0.00, 31.57, 40.10, 50.94, 64.72, 82.22, 104.45,
0.00, 0.00, 0.00, 0.00, 24.85, 31.57, 40.10, 50.94, 64.72,
0.00, 0.00, 0.00, 0.00, 0.00, 19.56, 24.85, 31.57, 40.10,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 15.40, 19.56, 24.85,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 12.12, 15.40,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 9.54),
nrow = 9, byrow = TRUE
)
valor_euro_put_MU <- matrix(
c(13.34, 8.05, 3.77, 1.05, 0.00, 0.00, 0.00, 0.00, 0.00,
0.00, 18.17, 11.91, 6.19, 1.97, 0.00, 0.00, 0.00, 0.00,
0.00, 0.00, 23.95, 17.07, 9.95, 3.71, 0.00, 0.00, 0.00,
0.00, 0.00, 0.00, 30.40, 23.57, 15.53, 6.97, 0.00, 0.00,
0.00, 0.00, 0.00, 0.00, 36.96, 31.01, 23.22, 13.10, 0.00,
0.00, 0.00, 0.00, 0.00, 0.00, 42.92, 38.40, 32.43, 24.62,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 47.80, 44.41, 39.87,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 51.83, 49.32,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 55.18),
nrow = 9, byrow = TRUE
)
# Datos para la opción put Americana de MU
precio_amer_put_MU <- matrix(
c(64.72, 82.22, 104.45, 132.69, 168.57, 214.15, 272.06, 345.62, 439.08,
0.00, 50.94, 64.72, 82.22, 104.45, 132.69, 168.57, 214.15, 272.06,
0.00, 0.00, 40.10, 50.94, 64.72, 82.22, 104.45, 132.69, 168.57,
0.00, 0.00, 0.00, 31.57, 40.10, 50.94, 64.72, 82.22, 104.45,
0.00, 0.00, 0.00, 0.00, 24.85, 31.57, 40.10, 50.94, 64.72,
0.00, 0.00, 0.00, 0.00, 0.00, 19.56, 24.85, 31.57, 40.10,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 15.40, 19.56, 24.85,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 12.12, 15.40,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 9.54),
nrow = 9, byrow = TRUE
)
valor_amer_put_MU <- matrix(
c(14.29, 8.54, 3.98, 1.10, 0.00, 0.00, 0.00, 0.00, 0.00,
0.00, 19.55, 12.64, 6.54, 2.07, 0.00, 0.00, 0.00, 0.00,
0.00, 0.00, 25.91, 18.16, 10.52, 3.90, 0.00, 0.00, 0.00,
0.00, 0.00, 0.00, 33.15, 25.13, 16.44, 7.33, 0.00, 0.00,
0.00, 0.00, 0.00, 0.00, 39.87, 33.15, 24.62, 13.78, 0.00,
0.00, 0.00, 0.00, 0.00, 0.00, 45.16, 39.87, 33.15, 24.62,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 49.32, 45.16, 39.87,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 52.60, 49.32,
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 55.18),
nrow = 9, byrow = TRUE
)
# Función simplificada para visualizar árbol binomial
plot_simple_tree <- function(prices, values, title) {
# Dimensiones
n_rows <- nrow(prices)
n_cols <- ncol(prices)
# Crear un gráfico vacío
plot(0, 0, type = "n",
xlim = c(0, n_cols-1), ylim = c(-(n_rows-1), 0),
xlab = "Trimestre", ylab = "Nivel",
main = title)
# Dibujar cada nodo
for(i in 1:n_rows) {
for(j in 1:n_cols) {
if(prices[i, j] > 0) {
# Coordenadas
x <- j-1
y <- -(i-1)
# Cambio porcentual desde el precio inicial
change_pct <- (prices[i, j] / prices[1, 1] - 1) * 100
# Determinar color basado en el cambio porcentual
if(change_pct > 0) {
node_color <- rgb(0, 0.8, 0, alpha = 0.7) # Verde para subidas
} else if(change_pct < 0) {
node_color <- rgb(0.8, 0, 0, alpha = 0.7) # Rojo para bajadas
} else {
node_color <- rgb(0.8, 0.8, 0, alpha = 0.7) # Amarillo para sin cambio
}
# Tamaño basado en el valor de la opción (si existe)
point_size <- 1
if(!is.na(values[i, j]) && values[i, j] > 0) {
# Escalar tamaño entre 1 y 3 según el valor
max_val <- max(values[values > 0], na.rm = TRUE)
point_size <- 1 + 2 * (values[i, j] / max_val)
}
# Dibujar el nodo
points(x, y, pch = 16, col = node_color, cex = point_size)
# Añadir texto con precio y valor de la opción
if(!is.na(values[i, j])) {
text(x, y + 0.2, sprintf("%.0f", prices[i, j]), cex = 0.8)
text(x, y - 0.2, sprintf("(%.1f)", values[i, j]), cex = 0.8, col = "blue")
} else {
text(x, y, sprintf("%.0f", prices[i, j]), cex = 0.8)
}
# Conectar con líneas a los nodos hijos (si existen)
if(j < n_cols) {
if(i < n_rows && prices[i+1, j+1] > 0) {
lines(c(x, x+1), c(y, y-1), col = "gray")
}
if(prices[i, j+1] > 0) {
lines(c(x, x+1), c(y, y), col = "gray")
}
}
}
}
}
# Añadir información
legend_x <- n_cols * 0.05
legend_y <- -n_rows * 0.9
legend_text <- sprintf(
"Precio Inicial: $%.2f\nValor de la Opción: $%.2f",
prices[1, 1], values[1, 1]
)
text(legend_x, legend_y, legend_text, adj = c(0, 0), cex = 0.8)
# Leyenda de colores
legend(n_cols * 0.6, legend_y,
legend = c("Subida", "Sin cambio", "Bajada"),
col = c(rgb(0, 0.8, 0, alpha = 0.7),
rgb(0.8, 0.8, 0, alpha = 0.7),
rgb(0.8, 0, 0, alpha = 0.7)),
pch = 16, pt.cex = 1.5, cex = 0.8, horiz = TRUE)
}
# Usar la función con los datos de la opción put Europea
plot_simple_tree(
precio_euro_put_MU,
valor_euro_put_MU,
"Árbol Binomial: Opción Put Europea para MU"
)
# Usar la función con los datos de la opción put Americana
plot_simple_tree(
precio_amer_put_MU,
valor_amer_put_MU,
"Árbol Binomial: Opción Put Americana para MU"
)
El portafolio está concentrado en NVIDIA (93.36%), con pequeñas
participaciones en APH (6.05%) y MU (0.58%), lo que representa un alto
riesgo de concentración. El rendimiento esperado es del 84.15%, pero con
una volatilidad de 104.14%, lo que indica un riesgo extremo. Las caídas
recientes de las acciones justifican la estrategia de cobertura. Se
implementó una cobertura con opciones put para MU, aunque MU representa
solo el 0.58% del portafolio. Las opciones put europeas y americanas
tienen una pequeña diferencia de precio, lo que sugiere la posibilidad
de usar una estrategia mixta. Recomendaciones Estratégicas: diversificar
el portafolio para reducir el riesgo de concentración, ampliar la
cobertura a NVIDIA, que también ha tenido una caída significativa,
implementar un programa de rolling de opciones trimestral y monitorear
las correlaciones entre activos. El portafolio está optimizado para el
rendimiento, pero su alta concentración aumenta el riesgo. La estrategia
de cobertura es adecuada, pero debe extenderse a NVIDIA y mejorar con
opciones mixtas. Las visualizaciones adicionales ayudarían a evaluar
mejor el impacto de la cobertura.