Taller 1

Jenyfer Martínez Solarte - 2401795
Jessica Paola Mosquera Sánchez - 2508168
Karen Andrea Calvache Londoño - 2508272
Daniel Fernando Buitron Ruiz - 2502935


1. Introducción

Este reporte documenta el flujo de trabajo para la obtención, limpieza y normalización de un dataset de 7 activos financieros del sector entretenimiento y tecnología: Disney (DIS), Warner Bros. Discovery (WBD), Paramount (PSKY), Fox (FOX), Netflix (NFLX), Sony (SONY) y Amazon (AMZN).

Se ha seleccionado la Ruta A, abarcando un periodo desde inicios de 2021 hasta 2026 para garantizar una ventana de análisis robusta.

2. Diccionario de Datos (Metadata)

Antes de proceder al análisis, definimos técnicamente cada variable para asegurar la reproducibilidad y el entendimiento del dataset final.

Variable Descripción Unidad / Moneda Tipo Llave / Nivel Fuente
date Fecha de cierre de la sesión bursátil YYYY-MM-DD Date (Compuesta) Yahoo Finance
ticker Símbolo identificador del activo Texto String (Compuesta) Yahoo Finance
price Precio de cierre ajustado (\(P_t\)) USD Numeric Observación Yahoo Finance
volume Cantidad de acciones transaccionadas Unidades Numeric Observación Yahoo Finance
retorno Variación porcentual diaria Decimal Numeric Derivada Cálculo Propio
volatilidad Riesgo medido por Desviación Estándar (21d) Decimal Numeric Derivada Cálculo Propio

3. Implementación del Flujo de Datos

1. CARGA DE LIBRERÍAS

library(quantmod)
library(tidyverse)
library(knitr)

2. PARÁMETROS

tickers <- c('DIS', 'WBD', 'PSKY', 'FOX', 'NFLX', 'SONY', 'AMZN')
inicio <- "2021-01-01"
fin <- "2026-02-01"

3. DESCARGA AUTOMATIZADA (Trazabilidad)

datos_raw <- map_df(tickers, function(x) {
  df_temp <- getSymbols(x, src = "yahoo", from = inicio, to = fin, auto.assign = FALSE)
  data.frame(
    date = index(df_temp),
    ticker = x,
    price = as.numeric(Ad(df_temp)),
    volume = as.numeric(Vo(df_temp))
  )
})

4. TRANSFORMACIÓN: CAMBIO DE NOMBRES Y CÁLCULOS

Renombramos los tickers a nombres reales Cálculo de Retorno Simple: \(R_t = \frac{P_t - P_{t-1}}{P_{t-1}}\)

dataset_analitico <- datos_raw %>%
  group_by(ticker) %>%
  arrange(date) %>%
  mutate(
    # Recodificación de nombres para mejor legibilidad
    ticker = recode(ticker, 
                    "DIS"  = "Disney",
                    "WBD"  = "Warner Bros",
                    "PSKY" = "Paramount",
                    "FOX"  = "Fox",
                    "NFLX" = "Netflix",
                    "SONY" = "Sony",
                    "AMZN" = "Amazon"),
    # Cálculo de métricas financieras
    retorno = (price / lag(price)) - 1,
    volatilidad = runSD(retorno, n = 21)
  ) %>%
  ungroup()

5. PROCESO DE LIMPIEZA DE CALIDAD (Auditoría de NAs y Errores)

nas_detectados <- sum(is.na(dataset_analitico$volatilidad))

data_bolsa <- dataset_analitico %>%
  drop_na(price, retorno, volatilidad) %>% # 1. Eliminar precios nulos y NAs por cálculo
  filter(price > 0) %>%                   # 2. Solo precios reales
  distinct(ticker, date, .keep_all = TRUE) # 3. Garantizar llave única

5.1.—– CHECKS DE CALIDAD ——

Check 1: Ver el tipo de dato de cada columna

print(str(data_bolsa))
## tibble [8,778 × 6] (S3: tbl_df/tbl/data.frame)
##  $ date       : Date[1:8778], format: "2021-02-03" "2021-02-03" ...
##  $ ticker     : chr [1:8778] "Disney" "Warner Bros" "Paramount" "Fox" ...
##  $ price      : num [1:8778] 172.4 40.8 46.3 27 53.9 ...
##  $ volume     : num [1:8778] 5508600 7654200 13200000 2419300 31723000 ...
##  $ retorno    : num [1:8778] -0.003 0.0182 0.0358 -0.0135 -0.0159 ...
##  $ volatilidad: num [1:8778] 0.0196 0.0346 0.0483 0.0394 0.0443 ...
## NULL

Se verificó mediante la función str() que la columna date fuera reconocida como objeto cronológico y price como numérico. Esto evita errores de cálculo en el Taller 2.

Check 2: Verificación de Llave Única

duplicados <- data_bolsa %>%
  count(ticker, date) %>%     # Cuenta cuántas veces aparece cada combinación
  filter(n > 1)               # Filtra las que aparezcan más de una vez

Resultado esperado: 0 filas

if(nrow(duplicados) == 0) {
  print("Check superado: No hay duplicados en la llave (ticker + date)")
  } else {
  print(paste("ALERTA: Se encontraron llaves duplicadas. ","Existen ", nrow(duplicados)," filas con llave duplicada."))
  }
## [1] "Check superado: No hay duplicados en la llave (ticker + date)"

Se definió una llave compuesta por ticker y date (Date). Se realizó una prueba de integridad consistente en el conteo de frecuencia para la combinación ticker + date. Se confirmó que no existen registros duplicados, asegurando que cada precio corresponde a una única sesión de mercado por activo.

Check 3: ¿Hay NAs? Conteo de NAs por columna

nulos <- colSums(is.na(data_bolsa))[colSums(is.na(data_bolsa))>0]

mensaje <-paste("ALERTA: En la columna ", names(nulos), "se encontraron ", nulos, " NAs.")
cat(mensaje, sep ="/n")
## ALERTA: En la columna   se encontraron    NAs.

Los NAs se detectaron en la primera fila de cada activo debido al cálculo del retorno

Check 4: Consistencia de Moneda Verificamos los precios promedio para asegurar que están en la misma escala

data_bolsa %>%
  group_by(ticker) %>%
  summarise(
    precio_medio = mean(price),
    moneda_estimada = "USD (Verificado por Exchange: NYSE/NASDAQ)"
  )
## # A tibble: 7 × 3
##   ticker      precio_medio moneda_estimada                           
##   <chr>              <dbl> <chr>                                     
## 1 Amazon             165.  USD (Verificado por Exchange: NYSE/NASDAQ)
## 2 Disney             116.  USD (Verificado por Exchange: NYSE/NASDAQ)
## 3 Fox                 36.2 USD (Verificado por Exchange: NYSE/NASDAQ)
## 4 Netflix             60.6 USD (Verificado por Exchange: NYSE/NASDAQ)
## 5 Paramount           20.5 USD (Verificado por Exchange: NYSE/NASDAQ)
## 6 Sony                20.0 USD (Verificado por Exchange: NYSE/NASDAQ)
## 7 Warner Bros         17.3 USD (Verificado por Exchange: NYSE/NASDAQ)

Moneda Única: Se verificó que todos los activos seleccionados (DIS, WBD, PSKY, FOX, NFLX, SONY, AMZN) corresponden a instrumentos listados en bolsas estadounidenses (NYSE y NASDAQ). Por lo tanto, los precios (price) están expresados uniformemente en Dólares Americanos (USD).

Unidades de Volumen: La variable volume se expresa en unidades de acciones transaccionadas por sesión.

Transformación: No se requirieron conversiones de tipo de cambio, asegurando que el cálculo del retorno sea puramente por variación de precio y no por riesgo cambiario.

Check 5: Rangos Lógicos ¿Hay precios negativos o volúmenes menores a cero?

errores_rango <- data_bolsa %>% filter(price <= 0 | volume < 0| retorno > 1 | retorno < -1)
if(nrow(errores_rango) == 0) {
  print("Check superado: Todos los valores están dentro de rangos lógicos.")
} else {
  print(paste("ALERTA: Existen ", nrow(errores_rango)," filas con valores negativos o precio cero."))
}
## [1] "Check superado: Todos los valores están dentro de rangos lógicos."

— 5.2 SECCIÓN: CONTABILIDAD DE CALIDAD —

  1. Punto de partida (Raw)
filas_iniciales <- nrow(dataset_analitico)
  1. Conteo de NAs técnicos (Los 21 días por activo + el primer lag)
filas_perdidas_nas <- sum(is.na(dataset_analitico$volatilidad) | is.na(dataset_analitico$retorno)) # Cada ticker pierde 21 días. 21 * 7 tickers = 147 filas aprox.
  1. Conteo de Errores Lógicos (Precios <= 0 o Volumen < 0)
filas_errores_logicos <- dataset_analitico %>% 
  filter(price <= 0 | volume < 0) %>% 
  nrow()
  1. Conteo de Duplicados
filas_duplicadas <- sum(duplicated(dataset_analitico[, c("ticker", "date")]))
  1. Dataset Final
filas_finales <- nrow(data_bolsa)

5.3 — TABLA DE AUDITORÍA —

## [1] "=== BITÁCORA DE ELIMINACIÓN DE REGISTROS ==="
##                                    Etapa Registros              Estado
## 1          Datos Iniciales (Descargados)      8925               Bruto
## 2   NAs Eliminados (Cálculo Retorno/Vol)     - 147    Limpieza Técnica
## 3 Errores Lógicos (Precios/Volumen <= 0)       - 0    Limpieza Calidad
## 4                  Duplicados Eliminados       - 0 Limpieza Integridad
## 5    Dataset Final (Listo para análisis)      8778                  OK

5.4 Gráfico rápido de consistencia

5.5 BITÁCORA DE TRAZABILIDAD

Resumen del Proceso de Calidad:

  • Normalización: Se reemplazaron satisfactoriamente los códigos técnicos por nombres comerciales, mejorando la visualización.

  • Missing Controlado: Se eliminaron r nas_detectados registros correspondientes al inicio de la serie (ventana de 21 días para volatilidad). No se detectaron NAs en el cuerpo de la serie.

  • Unidades: Se confirmó que el 100% de los activos cotizan en Dólares (USD) en los mercados NYSE y NASDAQ.

  • Integridad: La llave primaria (ticker + date) es única para cada observación, eliminando el riesgo de duplicidad en los cálculos futuros.

6. VISTA PREVIA DEL DATA SET FINAL

Este es el aspecto de los datos que se utilizarán en el Taller 2.

date ticker price volume retorno volatilidad
2021-02-03 Disney 172.37077 5508600 -0.0029951 0.0195962
2021-02-03 Warner Bros 40.81000 7654200 0.0182136 0.0345545
2021-02-03 Paramount 46.28688 13200000 0.0358140 0.0483199
2021-02-03 Fox 27.02439 2419300 -0.0135365 0.0394116
2021-02-03 Netflix 53.94500 31723000 -0.0158896 0.0442794
2021-02-03 Sony 21.65997 15131000 0.1216244 0.0306843
2021-02-03 Amazon 165.62650 141776000 -0.0199616 0.0190394
2021-02-04 Disney 176.08334 9035400 0.0215383 0.0201457
2021-02-04 Warner Bros 41.11000 7120900 0.0073511 0.0344161
2021-02-04 Paramount 46.28688 18711000 0.0000000 0.0484569

Gráficos

Gráfico de la data

1. Gráfico de Retornos Diarios (Facetas)

2. Retorno Acumulado (Crecimiento de 1 USD)

Para este gráfico, primero calculamos el retorno acumulado agrupando por ticker.

# 1. Calculamos el acumulado (Base 1 USD)
df_acumulado <- data_bolsa %>%
  group_by(ticker) %>%
  arrange(date) %>% # Ordenar cronológicamente para el cálculo
  mutate(Retorno_Acumulado = cumprod(1 + retorno)) %>%
  ungroup()

# 2. Gráfico comparativo único
ggplot(df_acumulado, aes(x = date, y = Retorno_Acumulado, color = ticker)) +
  geom_line(size = 0.7) +
  # Línea de referencia en 1 (punto de equilibrio-sin ganancias ni pérdidas)
  geom_hline(yintercept = 1, linetype = "dashed", color = "black") +
  # Ejes: Años y Formato Moneda
  scale_x_date(date_labels = "%Y", date_breaks = "1 year") +
  scale_y_continuous(labels = scales::dollar_format()) +
  theme_minimal() +
  labs(
    title = "Crecimiento de 1 USD Invertido",
    subtitle = "Comparativa de rendimiento acumulado entre activos",
    x = "Año de Inversión",
    y = "Valor de la Inversión (USD)",
    color = "Empresa"
  ) +
  theme(legend.position = "bottom")


Taller 2

Este código se puede editar para lograr visualizar el top N de empresas, reduciendo el ruido visual y enfocandosé en las N más volátiles.

topN <- 7
top_empresa <- data_bolsa %>%
  group_by(ticker) %>%
  summarise(sd_ret = sd(retorno), .groups = "drop") %>%
  arrange(desc(sd_ret)) %>%
  slice_head(n = topN) %>%
  pull(ticker)
R_top <- data_bolsa %>% filter(ticker %in% top_empresa)

KPIs Básicos y Cola

Tabla resumen por estrategia: tamaño muestral, tendencia central, volatilidad, asimetría/curtosis y percentiles clave (colas), ordenado por p05 (downside).

## # A tibble: 7 × 14
##   ticker       n     mean      med     sd    skew  kurt     p01     p05      p50
##   <chr>    <int>    <dbl>    <dbl>  <dbl>   <dbl> <dbl>   <dbl>   <dbl>    <dbl>
## 1 Warner …  1254  3.27e-4 -3.41e-4 0.0353  0.161  12.3  -0.0894 -0.0491 -3.41e-4
## 2 Paramou…  1254 -4.70e-4 -6.96e-4 0.0355  0.321  21.2  -0.0813 -0.0477 -6.96e-4
## 3 Netflix   1254  7.10e-4  4.10e-4 0.0268 -1.76   32.5  -0.0674 -0.0351  4.10e-4
## 4 Amazon    1254  5.21e-4  4.18e-4 0.0221  0.140   8.18 -0.0561 -0.0325  4.18e-4
## 5 Sony      1254  2.74e-4 -2.04e-4 0.0183  0.419   6.46 -0.0463 -0.0269 -2.04e-4
## 6 Disney    1254 -1.73e-4 -2.45e-4 0.0183 -0.0296 10.6  -0.0461 -0.0268 -2.45e-4
## 7 Fox       1254  8.33e-4  6.96e-4 0.0168  0.0695  6.61 -0.0450 -0.0251  6.96e-4
## # ℹ 4 more variables: p95 <dbl>, p99 <dbl>, min <dbl>, max <dbl>

Distribución de Colas

Bloque para “ver” forma de retornos: dispersión, colas, outliers y qué tan lejos está la data de una normalidad ideal

Ridgelines (densidades apiladas): Ordeno estrategias por volatilidad y comparo densidades apiladas. Útil para ver asimetría y colas (especialmente a la izquierda).

Raincloud (halfeye + box + puntos): “Todo en uno”, forma (halfeye), resumen robusto (boxplot) y nube de puntos. Muy útil para comparar mediana, dispersión y outliers sin asumir normalidad.

ECDF (cola izquierda enfocada): CDF empírica para leer probabilidades directas: P(Retorno ≤ x) probabilidad de que un retorno sea inferior a un valor de referencia o mínimo X. Útil para comparar downside risk entre empresas.

Q-Q plot vs Normal: Comparo cuantiles empíricos vs cuantiles normales (en z-scores). Si se despega en colas, queda evidente la no-normalidad (colas gordas/asimetría).

Outliers y Eventos Extremos

Aquí se enfoca en extremos: qué tan frecuentes son los eventos raros y qué tan malos han sido (tail risk / event risk).

Outliers robustos por empresa usando MAD: MAD = Median Absolute Deviation (Desviación Absoluta Mediana). Se calcula como: MAD = mediana(|retorno - mediana(retorno)|). Es una medida de dispersión.

ROBUSTA: no se deja sesgar por outliers como sí pasa con la desviación estándar. Aquí se estima un “z-score robusto”: robust_z = (retorno - mediana)/MAD, y se marca como outlier los puntos con |robust_z| > 5. Luego, se resume cuántos outliers hay, su tasa, y dos referencias de cola: el peor retorno (min) y el percentil 5% (worst_5).

## # A tibble: 7 × 5
##   ticker      outliers out_rate worst_1 worst_5
##   <chr>          <int>    <dbl>   <dbl>   <dbl>
## 1 Netflix           42   0.0335 -0.351  -0.0351
## 2 Paramount         35   0.0279 -0.284  -0.0477
## 3 Fox               26   0.0207 -0.0962 -0.0251
## 4 Disney            25   0.0199 -0.132  -0.0268
## 5 Amazon            24   0.0191 -0.140  -0.0325
## 6 Warner Bros       24   0.0191 -0.274  -0.0491
## 7 Sony              19   0.0152 -0.0717 -0.0269

Peores 10 retornos por estrategia Se sacan los 10 días más malos por empresa para visualizar event risk.

Dinámica temporal

Vol rolling, drawdowns: Ahora se mira el tiempo: volatilidad en ventanas móviles, crisis vs calma,y drawdowns (caídas máximas) como lectura clásica financiera.

Volatilidad rolling (12 meses): Se selecciona 3 empresas (fijas) para que el gráfico sea reproducible. Se calcula SD móvil de 12 meses para ver cambios de régimen y clustering.

Drawdowns (EDA financiero clásico): Este panel muestra el rendimiento acumulado , drawdowns y riesgo (las peores rachas de pérdidas).

Dependencia: Correlación

Co-movimiento entre estrategias. Primera lectura de diversificación: quién se mueve con quién (linealmente). Si la correlación es alta (cercana a 1), no hay diversificación; si una cae, la otra probablemente también.

Bootstrap para KPIs + Incertidumbre

El Bootstrap simula miles de escenarios posibles para ver qué tan “seguro” es nuestro cálculo de retorno.(margeb)

Error bars: media con IC: Comparar retorno promedio con incertidumbre (IC 95%).

Error bars: p05 con IC: Mismo enfoque, pero ahora para riesgo de cola (percentil 5%).

Panel Final

Se arma un dashboard estático con 4 gráficos clave para llevar a slideso para una lectura rápida sin mil ventanas abiertas.