#Instalamos los respectivos paquetes para correr todos los codigos

library(readxl)
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr)
library(ggplot2)
library(tseries)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo

#Leemos el archivo con los datos sacados de Bloomberg, con sus respectivas acciones a 10 años

Acciones_raw <- read_excel("C:/Users/User/OneDrive/Escritorio/acciones.xlsx")
## New names:
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...14`

Acciones seleccionadas

TXN – Sector tecnológico. Expectativas positivas por expansión en semiconductores.
PEP – Sector consumo. Estabilidad y flujo de caja constante.
UPS – Sector transporte y logística. Recuperación post-pandemia y mejora en márgenes.

Estas empresas presentan diversificación sectorial y volatilidades moderadas, ideales para conformar un portafolio equilibrado.

#Detectar las columnas con formato de fecha

is_excel_date_col <- function(x){
  num <- suppressWarnings(as.numeric(x))
  mean(!is.na(num) & num > 30000 & num < 50000) > 0.5
}

is_date <- sapply(Acciones_raw, is_excel_date_col)
date_idxs <- which(is_date)
pairs <- data.frame(date_idx = date_idxs, price_idx = date_idxs + 1)
pairs <- pairs[pairs$price_idx <= ncol(Acciones_raw), ]

pairs
##     date_idx price_idx
## TXN        1         2
## PEP        6         7
## UPS       11        12

#Creamos la base de datos limpia solo con fechas y precios de cada acción

nm <- names(Acciones_raw)

list_tbls <- lapply(1:nrow(pairs), function(i){
  di <- pairs$date_idx[i]
  pi <- pairs$price_idx[i]

  ticker_name <- nm[pi]
  ticker_name <- gsub("[^A-Za-z0-9]", "", ticker_name)

  dates_raw <- Acciones_raw[[di]]
  prices_raw <- Acciones_raw[[pi]]

  # Intentar convertir fechas (varios formatos posibles)
  dates_conv <- suppressWarnings(as.Date(as.numeric(dates_raw), origin = "1899-12-30"))
  if(all(is.na(dates_conv))){
    dates_conv <- suppressWarnings(as.Date(as.character(dates_raw), format = "%Y-%m-%d"))
  }
  if(all(is.na(dates_conv))){
    dates_conv <- suppressWarnings(as.Date(as.character(dates_raw), format = "%d/%m/%Y"))
  }

  prices_conv <- suppressWarnings(as.numeric(gsub(",", "", as.character(prices_raw))))

  data.frame(Date = dates_conv, Price = prices_conv, Ticker = ticker_name)
})

# --- Corrección principal ---
# Unir todas las tablas y asegurar que sea data.frame limpio
df_long <- bind_rows(list_tbls)

df_wide <- df_long %>%
  pivot_wider(names_from = Ticker, values_from = Price) %>%
  arrange(Date)

# 🔧 Forzar a data.frame y eliminar atributos ts/mts
df_wide <- as.data.frame(df_wide)
attr(df_wide, "tsp") <- NULL
class(df_wide) <- setdiff(class(df_wide), c("ts", "mts"))

# Limpiar fechas vacías y duplicadas (usando dplyr de forma explícita)
Acciones <- df_wide %>%
  dplyr::filter(!is.na(Date)) %>%
  dplyr::distinct(Date, .keep_all = TRUE)

# Renombrar columnas (por claridad)
if (ncol(Acciones) >= 4) {
  names(Acciones)[1:4] <- c("Date", "TXN", "PEP", "UPS")
}

head(Acciones)
##         Date    TXN    PEP    UPS
## 1 2014-08-29 48.180  92.49  97.33
## 2 2014-09-30 47.690  93.09  98.29
## 3 2014-10-31 49.660  96.17 104.91
## 4 2014-11-28 54.420 100.10 109.92
## 5 2014-12-31 53.465  94.56 111.17
## 6 2015-01-30 53.450  93.78  98.84

#Calcular retornos logarítmicos

retornos <- data.frame(
  Date = Acciones$Date[-1],
  ret_TXN = diff(log(Acciones$TXN)),
  ret_PEP = diff(log(Acciones$PEP)),
  ret_UPS = diff(log(Acciones$UPS))
)

head(retornos)
##         Date       ret_TXN      ret_PEP      ret_UPS
## 1 2014-09-30 -0.0102222649  0.006466237  0.009815026
## 2 2014-10-31  0.0404780478  0.032550692  0.065180547
## 3 2014-11-28  0.0915319533  0.040052228  0.046649989
## 4 2014-12-31 -0.0177044991 -0.056935133  0.011307733
## 5 2015-01-30 -0.0002805967 -0.008282940 -0.117558180
## 6 2015-02-27  0.0953952174  0.053966196  0.028819864

#Medias, desviaciones y matrices de covarianza/correlación

ret_mat <- as.matrix(retornos[, -1])

medias <- colMeans(ret_mat, na.rm = TRUE)
desv <- apply(ret_mat, 2, sd, na.rm = TRUE)

# Matrices
cov_mat <- cov(ret_mat, use = "complete.obs")
cor_mat <- cor(ret_mat, use = "complete.obs")

medias
##       ret_TXN       ret_PEP       ret_UPS 
##  9.272468e-03  3.614436e-03 -7.474693e-05
desv
##    ret_TXN    ret_PEP    ret_UPS 
## 0.06731444 0.04350410 0.07583505
cov_mat
##             ret_TXN     ret_PEP     ret_UPS
## ret_TXN 0.004531234 0.001067204 0.002358746
## ret_PEP 0.001067204 0.001892607 0.001227548
## ret_UPS 0.002358746 0.001227548 0.005750955
cor_mat
##           ret_TXN   ret_PEP   ret_UPS
## ret_TXN 1.0000000 0.3644258 0.4620648
## ret_PEP 0.3644258 1.0000000 0.3720816
## ret_UPS 0.4620648 0.3720816 1.0000000

##Valores mostrados en anual

medias_anual <- medias * 252
desv_anual <- desv * sqrt(252)
medias_anual
##     ret_TXN     ret_PEP     ret_UPS 
##  2.33666200  0.91083791 -0.01883623
desv_anual
##   ret_TXN   ret_PEP   ret_UPS 
## 1.0685837 0.6906062 1.2038441

#Portafolio de mínima varianza

opt <- portfolio.optim(ret_mat)

opt$pw           # Pesos óptimos
## [1] 0.16043577 0.77140070 0.06816353
opt$exp.ret      # Retorno esperado diario
## NULL
opt$ps           # Desviación estándar diaria
## [1] 0.04140535
# Anualizar
ret_port_anual <- opt$exp.ret * 252
sd_port_anual <- opt$ps * sqrt(252)

ret_port_anual
## numeric(0)
sd_port_anual
## [1] 0.6572895
SPX <- read_excel("C:/Users/User/OneDrive/Escritorio/acciones.xlsx")
## New names:
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...14`
SPX <- SPX %>%
  rename(Date = 1, Price = 2) %>%
  mutate(
    # Convertir fechas
    Date = as.Date(as.numeric(Date), origin = "1899-12-30"),
    # Limpiar comas o texto y convertir a número
    Price = as.numeric(gsub(",", "", as.character(Price)))
  ) %>%
  arrange(Date)
## Warning: There were 2 warnings in `mutate()`.
## The first warning was:
## ℹ In argument: `Date = as.Date(as.numeric(Date), origin = "1899-12-30")`.
## Caused by warning in `as.Date()`:
## ! NAs introducidos por coerción
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
head(SPX)
## # A tibble: 6 × 14
##   Date       Price ...3    ...4  ...5  PEP   ...7  ...8  ...9  ...10 UPS   ...12
##   <date>     <dbl> <chr>   <chr> <lgl> <chr> <chr> <chr> <chr> <lgl> <chr> <chr>
## 1 2014-08-29  48.2 986000… <NA>  NA    41880 92.49 9102… <NA>  NA    41880 97.33
## 2 2014-09-30  47.7 989200… <NA>  NA    41912 93.09 8690… <NA>  NA    41912 98.29
## 3 2014-10-31  49.7 242770… <NA>  NA    41943 96.17 1267… <NA>  NA    41943 104.…
## 4 2014-11-28  54.4 947100… <NA>  NA    41971 100.1 7541… <NA>  NA    41971 109.…
## 5 2014-12-31  53.5 120990… <NA>  NA    42004 94.56 9441… <NA>  NA    42004 111.…
## 6 2015-01-30  53.4 117800… <NA>  NA    42034 93.78 1122… <NA>  NA    42034 98.84
## # ℹ 2 more variables: ...13 <chr>, ...14 <chr>

#Calculo de las Betas con el índice S&P500

SPX <- read_excel("C:/Users/User/OneDrive/Escritorio/acciones.xlsx")
## New names:
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...14`
SPX <- SPX %>%
  rename(Date = 1, Price = 2) %>%
  mutate(Date = as.Date(as.numeric(Date), origin = "1899-12-30")) %>%
  arrange(Date)
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `Date = as.Date(as.numeric(Date), origin = "1899-12-30")`.
## Caused by warning in `as.Date()`:
## ! NAs introducidos por coerción
SPX$Price <- as.numeric(gsub("[^0-9.]", "", as.character(SPX$Price)))


spx_ret <- data.frame(Date = SPX$Date[-1],
                      ret_SPX = diff(log(SPX$Price)))

# Unir con los retornos de las acciones
merged <- merge(retornos, spx_ret, by = "Date")

# Calcular betas
betas <- sapply(c("ret_TXN", "ret_PEP", "ret_UPS"), function(col){
  coef(lm(as.formula(paste(col, "~ ret_SPX")), data = merged))[2]
})

betas
## ret_TXN.ret_SPX ret_PEP.ret_SPX ret_UPS.ret_SPX 
##       1.0000000       0.2355218       0.5205526

#Betas del protafolio

beta_port <- sum(opt$pw * betas)
beta_port
## [1] 0.3776001
S_indice <- 5500.00
Q_futuro <- 250 
C_portafolio <- 10000000
rf_anual <- 0.045

#Analisis de sensibilidad de betas

beta_alternativa_1 <- 0.8
beta_alternativa_2 <- 1.5

N_beta_0_8 <- (beta_alternativa_1 * C_portafolio) / (S_indice * Q_futuro)
N_beta_1_5 <- (beta_alternativa_2 * C_portafolio) / (S_indice * Q_futuro)


cat("\nAnalisis de Sensibilidad de Contratos:\n")
## 
## Analisis de Sensibilidad de Contratos:
cat("Si Beta 0.8:", round(N_beta_0_8, 2), "contratos.\n")
## Si Beta 0.8: 5.82 contratos.
cat("Si Beta  1.5:", round(N_beta_1_5, 2), "contratos.\n")
## Si Beta  1.5: 10.91 contratos.
# Una Beta menor (0.8) requiere menos contratos, lo que implica una menor cobertura de riesgo sistemático. 
# Una Beta mayor (1.5) requiere más contratos, lo que implica una mayor cobertura, pero también un mayor costo de margen.

#Calcular VaR histórico (5% y 1%)

ret_port <- as.numeric(ret_mat %*% opt$pw)

VaR_5 <- quantile(ret_port, probs = 0.05)
VaR_1 <- quantile(ret_port, probs = 0.01)

VaR_5
##          5% 
## -0.06565905
VaR_1
##          1% 
## -0.09499933

El Valor en Riesgo (VaR) mide la pérdida máxima que se espera para tu cartera en un día de negociación, bajo condiciones normales de mercado. VaR al 5% (-6.57%): Existe un 95% de probabilidad de que el portafolio no pierda más del 6.57% de su valor cada día. VaR al 1% (-9.50%): Aumentando la confianza, existe una probabilidad del 99% de que la pérdida diaria no exceda el 9.50% del valor del portafolio. Conclusión: Estos valores indican el capital que está en riesgo. Por ejemplo, si el portafolio tiene un valor de \(10,000,000 COP1\), el VaR al 5% sugiere que en 5 días por cada 100 la pérdida podría superar los $657,000 COP. Esta es la cifra de riesgo fundamental que se aplica para calcular los valores de coberturas.

Gráficos para el informe

# Retornos
ret_long <- retornos %>%
  pivot_longer(-Date, names_to = "Ticker", values_to = "Return")

ggplot(ret_long, aes(Date, Return, color = Ticker)) +
  geom_line(size = 0.8) +
  theme_minimal() +
  labs(title = "Retornos logaritmicos diarios", y = "Retorno diario")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

## Análisis fundamental TXN pertenece al sector tecnológico de semiconductores, con una expectativa positiva ante la creciente demanda de chips. PEP está en el sector de consumo básico, con un crecimiento estable y alta resistencia a la inflación. UPS, por su parte opera en el sector logístico, afectado por la desaceleración económica global pero con proyecciones de recuperación gradual. En conjunto, estas tres acciones se espera una tendencia lateral-alcista a julio de 2025.

#  Cálculo del Sharpe Ratio del portafolio óptimo
# Tasa libre de riesgo anual 
rf <- 0.045  

# Extraer los valores desde el objeto opt
ret_esperado <- opt$pm      # retorno promedio diario
desv_port <- opt$ps         # desviación estándar diaria

# Calcular el Sharpe Ratio anualizado
sharpe <- ((ret_esperado * 252) - rf) / (desv_port * sqrt(252))
sharpe
## [1] 1.5689
# Mostrar resumen general del portafolio
cat("Pesos óptimos del portafolio:\n")
## Pesos óptimos del portafolio:
print(opt$pw)
## [1] 0.16043577 0.77140070 0.06816353
cat("\nRetorno anual esperado:", round(ret_esperado * 252, 4))
## 
## Retorno anual esperado: 1.0762
cat("\nDesviacion anual:", round(desv_port * sqrt(252), 4))
## 
## Desviacion anual: 0.6573
cat("\nSharpe Ratio:", round(sharpe, 4))
## 
## Sharpe Ratio: 1.5689
ggplot(retornos, aes(x = ret_TXN, y = ret_PEP)) +
  geom_point(alpha = 0.3, color = "black") +
  labs(title = "Correlacion entre TXN y PEP", x = "Retorno TXN", y = "Retorno PEP")

#visualización simple

Acc_long <- Acciones %>% 
  pivot_longer(-Date, names_to = "Ticker", values_to = "Price")

ggplot(Acc_long, aes(Date, Price, color = Ticker)) +
  geom_line(size = 1) +
  theme_minimal() +
  labs(title = "Evolucion de precios - TXN, PEP, UPS", y = "Precio (USD)")

#Conclusiones finales El portafolio elaborado muestra una buena diversificación entre sectores, combinando empresas de tecnología, consumo y transporte. Los retornos esperados se ajustan a lo que normalmente se observa en cada uno de estos sectores, lo que indica una selección coherente de activos. Además, la volatilidad del conjunto es menor que la de los activos por separado, lo que demuestra que la diversificación está cumpliendo su función al reducir el riesgo total. La beta del portafolio refleja una sensibilidad media frente al mercado, representado por el índice S&P 500, por lo que no se considera ni demasiado agresivo ni demasiado conservador. Por su parte, el VaR obtenido señala que el nivel de riesgo asumido es razonable en relación con el capital invertido. En conclusión, el portafolio cumple con los criterios de eficiencia y control del riesgo planteados, por lo que se puede considerar que la primera parte del trabajo final se encuentra correctamente desarrollada.

#Numero optimo de contratos

num_contratos_optimos <- (beta_port * C_portafolio) / (S_indice * Q_futuro)

cat("\nNúmero Óptimo de Contratos a Vender (N*):", round(num_contratos_optimos, 2), "\n")
## 
## Número Óptimo de Contratos a Vender (N*): 2.75

#Valor esperado de la cobertura trimestral

t_trimestral <- 3/12 

F_esperado <- S_indice * exp(rf_anual * t_trimestral)

ganancia_esperada_futuro <- (F_esperado - S_indice) * Q_futuro

VEC_trimestral <- num_contratos_optimos * ganancia_esperada_futuro

cat("\nValor Esperado de la Cobertura Trimestral (VEC):", round(VEC_trimestral, 2), "\n")
## 
## Valor Esperado de la Cobertura Trimestral (VEC): 42719.86