Este reporte documenta la transformación del Dataset Inicial (datos brutos de mercado) al Dataset Final listo para el uso en modelos.
Ventana: 2021-2026 (5 años).
Activos: 10 acciones.
# Identificar duplicados}
# 1. Definimos los activos (se pueden editar).
# Incluimos Acciones, ETFs e Índices
tickers <- c("AAPL", "MSFT", "GOOGL", "AMZN", "TSLA",
"NVDA", "META", "BRK-B", "V", "JPM",
"WMT", "MA", "PG", "UNH", "HD",
"BAC", "DIS", "ADBE", "NFLX", "XOM")
# 2. Descarga automática de los últimos 5 años
# tq_get descarga: symbol, date, open, high, low, close, volume, adjusted
raw_data_yahoo <- tq_get(tickers,
get = "stock.prices",
from = "2021-01-01",
to = "2026-02-17")
# 3. Adaptamos al formato de nuestro Dataset Analítico
raw_data <- raw_data_yahoo %>%
rename(ticker = symbol, price = adjusted) %>%
select(ticker, date, price, volume) %>%
mutate(currency = "USD") # Yahoo Finance entrega USD para estos tickers
# Vista previa del Dataset Inicial
kable(head(raw_data), caption = "Dataset Inicial (Muestra)")| ticker | date | price | volume | currency |
|---|---|---|---|---|
| AAPL | 2021-01-04 | 125.8567 | 143301900 | USD |
| AAPL | 2021-01-05 | 127.4128 | 97664900 | USD |
| AAPL | 2021-01-06 | 123.1239 | 155088000 | USD |
| AAPL | 2021-01-07 | 127.3252 | 109578200 | USD |
| AAPL | 2021-01-08 | 128.4242 | 105158200 | USD |
| AAPL | 2021-01-11 | 125.4385 | 100384500 | USD |
Este diccionario garantiza que cualquier usuario del dataset entienda el nivel y la unidad de medida:
| Variable | Descripcion | Unidad | Fuente | Llave |
|---|---|---|---|---|
| ticker | Simbolo del activo | Texto | Yahoo Finance | Si |
| date | Fecha de cierre | ISO 8601 | Yahoo Finance | Si |
| price | Precio ajustado | USD | Yahoo Finance | No |
| volume | Volumen diario | Unidades | Yahoo Finance | No |
| id_key | Llave unica | Texto | Generada | Si |
Buscamos duplicados para asegurar que no existan dos precios para el mismo activo el mismo día.
# Identificar duplicados
duplicados <- raw_data %>%
group_by(ticker, date) %>%
filter(n() > 1)
# Acción: Eliminar duplicados si existieran
clean_data <- raw_data %>%
distinct(ticker, date, .keep_all = TRUE)Bitácora: Se detectaron 0 duplicados. Acción: Se aplicó distinct() para asegurar la unicidad de la llave.
clean_data <- clean_data %>%
mutate(
date = as.Date(date),
price = as.numeric(price),
volume = as.numeric(volume),
ticker = as.character(ticker)
)Bitácora: Se validó que date sea clase Date y price/volume sean numéricos para permitir cálculos matemáticos.
# Conteo de NAs por columna
resumen_na <- colSums(is.na(clean_data))
cantidad_faltantes <- sum(resumen_na)
# Acción: Eliminar registros con precios faltantes
clean_data <- clean_data %>% drop_na(price)Bitácora: Se encontraron 0 valores nulos. Decisión: Eliminación de filas incompletas para evitar errores en el cálculo de retornos.
Validamos que los precios sean positivos y el volumen no sea negativo.
errores_logicos <- clean_data %>%
filter(price <= 0 | volume < 0)
# Filtrado de valores imposibles
clean_data <- clean_data %>%
filter(price > 0 & volume >= 0)Bitácora: Se detectaron 0 valores fuera de rango lógico. Acción: Eliminados del dataset final.
Creamos la llave final y presentamos el producto listo para su uso en modelos financieros.
final_dataset <- clean_data %>%
mutate(id_key = paste0(ticker, date)) %>%
select(id_key, ticker, date, price, volume, currency)
# Muestra aleatoria de 10 filas del dataset final
kable(final_dataset[sample(nrow(final_dataset), 10), ],
caption = "Muestra Aleatoria del Dataset Analítico Finalizado")| id_key | ticker | date | price | volume | currency |
|---|---|---|---|---|---|
| MSFT2023-08-08 | MSFT | 2023-08-08 | 319.20337 | 22327600 | USD |
| JPM2021-05-20 | JPM | 2021-05-20 | 142.46533 | 10426000 | USD |
| WMT2021-02-09 | WMT | 2021-02-09 | 45.41651 | 15906900 | USD |
| META2022-01-28 | META | 2022-01-28 | 299.61707 | 21871600 | USD |
| TSLA2025-05-14 | TSLA | 2025-05-14 | 347.67999 | 136997300 | USD |
| DIS2022-12-30 | DIS | 2022-12-30 | 84.88110 | 23231000 | USD |
| UNH2022-11-30 | UNH | 2022-11-30 | 512.96533 | 9688900 | USD |
| JPM2021-02-16 | JPM | 2021-02-16 | 127.38265 | 12130600 | USD |
| BAC2022-01-21 | BAC | 2022-01-21 | 40.34799 | 71303600 | USD |
| AAPL2025-10-17 | AAPL | 2025-10-17 | 251.81003 | 49147000 | USD |
Como última prueba de calidad, visualizamos la serie de tiempo para detectar anomalías visuales.
library(plotly)
# 1. Aseguramos que el ticker sea un factor para controlar el orden
final_dataset$ticker <- as.factor(final_dataset$ticker)
lista_tickers <- levels(final_dataset$ticker)
n_tickers <- length(lista_tickers)
# 2. Crear el gráfico base
p <- plot_ly(final_dataset, x = ~date, y = ~price, color = ~ticker,
type = 'scatter', mode = 'lines',
hovertemplate = "<b>%{fullData.name}</b><br>Precio: %{y:.2f} USD<extra></extra>")
# 3. Crear los botones de forma dinámica y sincronizada
botones <- lapply(1:n_tickers, function(i) {
# Creamos un vector de FALSE de tamaño n
visibilidad <- rep(FALSE, n_tickers)
# Solo la posición actual es TRUE
visibilidad[i] <- TRUE
list(
method = "restyle",
args = list("visible", visibilidad),
label = lista_tickers[i]
)
})
# 4. Añadir el botón "Todos"
botones <- c(list(list(
method = "restyle",
args = list("visible", rep(TRUE, n_tickers)),
label = "Todos los activos"
)), botones)
# 5. Configurar Layout
p %>% layout(
title = "Explorador Dinámico de Activos",
yaxis = list(title = "Precio (USD)", fixedrange = FALSE),
updatemenus = list(
list(
y = 0.9, x = -0.1,
active = 0, # Define que el primer botón (Todos) esté presionado al inicio
buttons = botones
)
)
)