library(readr)
library(dplyr)
library(tidyverse)
library(lubridate)
library(mice)
library(ggplot2)
library(plotly)
library(tidyr)
library(skimr)
library(naniar)
library(scales)
library(caret)
library(kknn)
library(class)
library(rpart)
library(rpart.plot)
library(e1071)
library(naivebayes)
pacman::p_load(
rio, here, janitor, epikit, naniar
)El mercado de valores es uno de los sistemas más complejos de la la economía moderna. El índice de S&P500, agrupa las 500 empresas de mayor capitulización en Estados Unidos, este es considerado como un termometro que representa la economía americana y, una referencia global del comportamiento financiero. No obstante, aunque en teoría, a largo plazo ofrece un rendimiento promedio de 10% anual, su comportamiento no es homogéneo a lo largo del tiempo: existen periodos de estabilidad, donde lo retornos son estables y la volatibilidad es baja y periodos de turbulencia que son conocidos como periodos de “crisis
La motivación de este proyecto surge de la pregunta financiera si: Es posible, a partir de varible macroeconómicas se puede encontrar en el mercado estados de estabilidad y crisis? Por tal razón, decidimos usar técnicas de minería de datos como: KNN, Arboles de decisión y Clasificador Bayesiano, para aprender patrones que diferencian regímenes el mercado y evaluar que tan bien estos modelos pueden clasificarlos.
Todos los datos utilizados en este proyecto fueron descargados de FRED (Federal Reserve Economic Data),el repositorio de series económicas y financieras de la Reserva Federal de los Estados Unidos. FRED es una fuente primaria, de acceso público utilizada para investigaciones académicas y análisis financiero profesional.
El periodo de análisis que utilizamos fue desde 8 de marzo de 2021 hasta el 27 de febrero de 2026, cubriendo así tanto periodos e recuperación post-pandemia, el ciclo e 2022-2023 con el alzamiento de tasas más agresivo de la Reserva Federal y la estabilización después del mercado. Este rango temporal es particularmente rico porque aunque es corto, contiene episodios bien diferenciados de estabilidad y crisis.
La selección de cada variable no fue arbitraria . Cada una fue escogida porque representa cierta dimensión distinta dentro del comportamiento del mercado y tiene su rol explicativo dentro de la literatura económica y financiera.
Descripción: Valor de cierre diario del índice S&P 500
EL S&P 500 es la variable central del estudio. Representa el nivel del mercado de renta variable estadounidense. Sus valores capturan el sentimiento general de los inversores, las expectativas de crecimiento corporativo y la reacción al nivel de riesgo que representa. Su comportamiento en un nivel elevado puede ser indice de estabilidad o confianza; caídas sostenidas señalan deterioro económico o incertidumbre. Es, en esencia, el objeto de estudio que queremos entender y clasificar.
Descripción: Índice de volatilidad implícita del mercado, calculado por el CBOE a partir de opciones sobre el S&P 500
El VIX s conocido como el “Indice del miedo”. Este mide la volatibilidad esperada por el mercado.Un VIX bajo (por debajo de 20) indica que los participantes del mercado anticipan un ambiente tranquilo; un VIX por encima de 20 refleja incertidumbre elevada, y valores superiores a 30 corresponden a episodios de alta tensión financiera. Esta variable cumple un rol especial en el proyecto: se utiliza como criterio de clasificación para definir si un periodo es de Estabilidad (VIX < 20) o de Crisis (VIX ≥ 20). Es importante destacar que, precisamente por este motivo, el VIX es excluido como predictor en los modelos de clasificación, lo cual se explica en detalle en la sección de metodología.
Descripción: Rendimiento del bono del Tesoro de los Estados Unidos a 10 años, expresado en porcentaje
La tasa de interés a largo plazo es uno de los indicadores macroeconómicos más importantes para los mercados financieros. Cuando la Reserva Federal sube la tasa de interes para combatir la inflación (como en el periodo de 2022 al 2023), el costo del crédito corporativo aumenta, las valuaciones de las acciones en el mercado caen, y los bonos se vuelven más competitivos. Esta variable captura el estado de la política monetaria y su presión sobre el mercado de acciones.
Descripcción: Tasa de inflación esperada a 10 años, derivada de los valores del Tesoro protegidos contra la inflación
Las expectativas de inflación afectan directamente las decisiones de inversión.Una inflación esperada elevada erosiona el valor real de los retornos futuros, genera presión sobre la Reserva Federal para subir tasas, y aumenta la incertidumbre macroeconómica. Durante el periodo 2021–2023, las expectativas de inflación jugaron un papel central en la volatilidad del S&P 500, lo que hace que esta variable sea especialmente relevante para nuestro análisis.
Descripcción: Valor del dólar frente a una canasta de monedas de los principales socios comerciales de Estados Unidos
La relación del dólar y el mercado es ciertamente compleja, un dólar fuerte puede perjudicar a las empresas exportadoras del S&P 500.Pero también, puede reflejar lo que se conoce como un “flight to safety”, la búsqueda de activos refugio en periodos de crisis global.
Aquí presentamos el diccionario completo de la base de datos, incluyendo las variables originales y las variables derivadas que se crean durante el proceso de preparación:
| Variable | Tipo | Descripción | Unidad | |
|---|---|---|---|---|
date |
Fecha | Fecha de observación | YYYY-MM-DD | |
SP500 |
Numérica | Valor de cierre del índice S&P 500 | Puntos | |
VIX |
Numérica | Índice de volatilidad implícita | Puntos | |
TASA |
Numérica | Rendimiento del Tesoro a 10 años | % anual | |
INFLA |
Numérica | Expectativas de inflación a 10 años | % anual | |
Dollar |
Numérica | Índice del valor del dólar | Índice | |
retorno_diario |
Numérica | Variación porcentual diaria del SP500 | % | |
volatilidad_movil |
Numérica | Desviación estándar de retornos (ventana 20 días) | ||
anio |
Entera | Año de la observación | Año | |
periodo |
Factor | Clasificación del periodo: Estabilidad / Crisis | Categórica |
retorno_diarioSe calcula como la variación porcentual del SP500 respecto al día
anterior usando lag(). Esta es la medida estándar de
retorno en finanzas. Es más informativa que el nivel del índice porque
nos dice cuánto ganó o perdió el mercado en un día, independientemente
del nivel absoluto del índice.
volatilidad_movilEs la desviación estándar de los últimos 20 retornos diarios. Una ventana de 20 días equivale aproximadamente a un mes de trading (días hábiles), que es el periodo convencional para medir volatilidad de corto plazo en finanzas. A mayor volatilidad móvil, mayor incertidumbre en el mercado.
periodoClasifica cada observación como “Estabilidad” o “Crisis” según si el VIX es menor o mayor que 20. Se eligió este umbral porque es el estándar ampliamente reconocido en la industria financiera: un VIX por debajo de 20 indica condiciones tranquilas, mientras que un VIX de 20 o más señala tensión. Valores superiores a 30 corresponden a episodios severos. Es importante destacar que aunque el VIX forma parte del dataset, se excluye como predictor en los modelos para evitar data leakage, ya que es la misma variable que define la etiqueta que los modelos intentan predecir. Este punto se desarrolla en la sección de metodología.
En esta sección presentamos tres gráficos diseñados para entender visualmente el comportamiento del SP500 a lo largo del tiempo y las diferencias entre periodos de Estabilidad y Crisis. Cada gráfico representa ciertas preguntas solo utilizando la base de datos, no hay ningún método de clasificación supervisada impuesta en los modelos.
plot_ly() %>%
# Línea base completa en gris (toda la serie continua)
add_lines(data = base_completa,
x = ~date,
y = ~SP500,
name = "SP500",
line = list(color = "#185FA5", width = 1),
showlegend = FALSE) %>%
# Puntos rojos encima para marcar los días de Crisis
add_markers(data = crisis,
x = ~date,
y = ~SP500,
name = "Crisis",
marker = list(color = "#E24B4A",
size = 3,
opacity = 0.6)) %>%
layout(
title = "Evolución del S&P 500 (2021-2026)",
xaxis = list(title = "Fecha",
type = "date",
tickformat = "%Y"),
yaxis = list(title = "Valor del índice (puntos)"),
legend = list(title = list(text = "Periodo"))
)Observación:
El gráfico muestra la trayectoria diaria del índice S&P 500 desde marzo 2021 hasta Febrero 2026. La linea azul representa la evolución del índice, mientras los puntos rojos identifican los días clasificados como crisis (VIX ≥ 20). Al observar la gráfica podemos ver tres episodios de Crisis:el primero entre 2021 y principios de 2022, coincidiendo con las primeras señales de presión post-pandemia; luego en el periodo de 2022 al 2023, que corresponde al ciclo más agresivo de la elevación de la tasa de interés, donde se puede ver que el S&P500 cayó casi hasta los 3,500 puntos; y por último el periodo de 2025 al 2026, que puede mostrar turbulencias nuevas, muy probale relacionadas a otras variables macroeconomicas. Después del 2024 se puede ver un movimiento alcista que permitió llegar hasta casi 7000 puntos en el 2026, aún así la gráfica muestra que en ese periodo hay señales de Crisis intercalados. Esta gráfica nos ayuda entender que los periodos de Crisis no son permanentes ni continuos sino episodios que se pueden identificar a lo largo del tiempo.
plot_ly(base_completa,
x = ~periodo,
y = ~retorno_diario,
color = ~periodo,
colors = c("Estabilidad" = "#185FA5",
"Crisis" = "#E24B4A")) %>%
add_boxplot() %>%
layout(
title = "Distribución del retorno diario por periodo",
xaxis = list(title = "Periodo"),
yaxis = list(title = "Retorno diario (%)"),
legend = list(title = list(text = "Periodo"))
)Observación:
El boxplot compara la distribución del retorno diario del S&P500 entre los periodos de Estabilidad y Crisis. Cuando observamos el boxplot de “Estabilidad” se ve que la caja es más estrecha y más centrada cerca del cero, lo que puede indicar que la mayoría de los días el mercado registra movimientos pequeños. Cuando vemos el boxplot de “Crisis” podemos ver que caja es más ancha y ligeramente a los valores negativos aunque no se nota mucho. Al ver ambos, podemos concluir que son presentaciones atípicas que llegan a más menos 30%. En un contexto de mercado financiero esto puede indicar algo normal ya que los movimientos más extremos pueden ocurrir en cualquier régimen, pero son más frecuentes y más negativos en Crisis.
p_scatter <- ggplot(base_completa,
aes(x = retorno_diario,
y = volatilidad_movil,
color = periodo)) +
geom_point(alpha = 0.5, size = 1.5) +
geom_smooth(method = "lm", linetype = "dashed", se = TRUE) +
scale_color_manual(values = c("Estabilidad" = "#185FA5",
"Crisis" = "#E24B4A")) +
labs(title = "Retorno diario vs Volatilidad móvil (20 días)",
x = "Retorno diario (%)",
y = "Volatilidad móvil (%)") +
theme_minimal()
ggplotly(p_scatter)Observación:
El diagrama de dispersión muestra la relación entre el retorno diario del S&P 500 y la volatilidad móvil de 20 días, diferenciando cada observación por periodo. La mayoría de los puntos se concentran cerca de retorno cero con volatilidades bajas, lo cual es normal — la mayor parte de los días de trading son tranquilos en cualquier régimen.
En esta parte estaremos exponiendo el proceso de codificaciones, desde la formación de la base de datos hasta la aplicación de los tres métodos de clasificación supervisada escogidos para analizar y contestar nuestras preguntas en base a nuestra investigación.
La base de datos utilizada en este análisis integra cinco series temporales diarias descargadas de FRED (Federal Reserve Economic Data),cubriendo el periodo del 8 de marzo de 2021 al 27 de febrero de 2026. Cada serie fue cargada de forma independiente para facilitar su inspección individual antes de ser integrada.
Descargar datos
Cada dataset se carga por separado desde su archivo CSV original
descargado de FRED. Usar head() y dim()
permite verificar rápidamente que los datos se cargaron correctamente —
head() muestra las primeras filas para confirmar el
formato, y dim() informa las dimensiones (filas y columnas)
de cada serie.
#Volatibilidad del mercado
VIX <- read_csv("~/Desktop/ESTA Mineria de Datos/VIXCLS.csv")
#Valor del SP500 diario
SP500 <- read_csv("~/Desktop/ESTA Mineria de Datos/SP500-2.csv")
#Tasa
TASA <- read_csv("~/Desktop/ESTA Mineria de Datos/DGS10-2.csv")
#Valor del dolar
Dollar <- read_csv("~/Desktop/ESTA Mineria de Datos/Dollar.csv")
# Inflacion
INFLA <- read_csv("~/Desktop/ESTA Mineria de Datos/T10YIE.csv")
head(SP500)## [1] 2606 2
## [1] 2652 2
## [1] 2652 2
as.Date() formato de fecha
Antes del merge, todas las columnas de fecha se convierten
explícitamente al tipo Date. Este paso es crítico: si una
serie tiene la fecha como texto y otra como Date, R no
puede alinearlas correctamente y el merge producirá un resultado vacío o
con errores. La función as.Date() garantiza que todas las
series hablen el mismo formato de fecha.
#Asegurar el formato de las fechas
SP500$observation_date <- as.Date(SP500$observation_date)
VIX$observation_date <- as.Date(VIX$observation_date)
TASA$observation_date <- as.Date(TASA$observation_date)
Dollar$observation_date <- as.Date(Dollar$observation_date)
INFLA$observation_date <- as.Date(INFLA$observation_date)Renombrar columna “Date”
Se estandarizan los nombres de columnas en todos los datasets antes
del merge. Todas las series de FRED vienen con la columna de fecha
llamada observation_date. Al renombrarla a
date de forma uniforme en todos los datasets, el merge por
"date" funciona sin ambigüedades.
merge()
Se integran las cinco series en un solo dataset usando
merge() con by = "date". Se utiliza un merge
interno porque queremos conservar únicamente las observaciones donde
todas las variables están presentes simultáneamente. Dado que las cinco
series pueden tener días distintos.
#Merge los datos
base <- merge(SP500, VIX, by= "date")
base <- merge(base, TASA, by= "date")
base <- merge(base, INFLA, by= "date")
base <- merge(base, Dollar, by= "date")
head(base)En esta parte vamos a explorar la base de datos e identificar los valores faltantes de la base de datos que acabos de formar. Como estamos trabajando con datos financieros diarios, podemos ver que la base en si tiene varios NAs, debido a que hay días que el mercado no esta operando por días feriados o finde semana, o simplemente la FRED no reportó el valor para esas fechas. Por eso ignorar los datos faltantes nos puede generar errores en la trayectoria de la investigación y resultados. Así que, debemos diagnosticar y luego aplicar alguna estrategia de imputación que persevere la distribución original de los datos.
Ver problemas y datos faltantes
Antes de imputar, es obligatorio ver la magnitud de los datos
faltantes dentro de la base, y también entender su patrón. Usaremos
gg_miss_var() que nos muestra cuántos NAs tiene cada
variable en términos absolutos. Por el otro lado vis_miss()
nos presenta un mapa visual que permite detectar si los NAs están
concentrados en fechas específicas o distribuidos
aleatoriamente. colSums(is.na()) cuantifica el número
exacto de faltantes por variable y después lo ponemos en porcentaje.
## [1] 2.987179
## date SP500 VIX TASA INFLA Dollar
## 0 49 20 55 55 54
## date SP500 VIX TASA INFLA Dollar
## 0.000000 3.769231 1.538462 4.230769 4.230769 4.153846
Observaciones:
La gráfica de gg_miss_var(base) gg_miss_var()`
muestra que las variables con mayor número de NAs son TASA (55), INFLA
(55) y Dollar (54), seguidas de SP500 (49) y VIX (20).
El mapa vis_miss() confirma que los NAs representan
solo el 3% del total de observaciones y que están distribuidos a lo
largo del dataset en fechas puntuales, así que no están concentrados en
un bloque continuo. Podemos entender que los NAs corresponden a ciertos
días en especificos donde no fueron reportados por la FRED, y no es que
hay un problema estructurar o de patrón en la base de datos.
El missigness fue muy bajo (menos de 5%), y los NAs estan dispersados en la base, así que hemos decidido irnos con el método de imputación por media utilizando MICE.
Imputación
Se utiliza MICE, con el método de media
(method = "mean") para reemplazar esos valores por el
promedio de la variable. Esta decisión es apropiada dado que el
porcentaje de datos faltantes es pequeño y el objetivo es preservar la
distribución general de los datos.
# Trabajar con procesamiento de datos faltantes (Imputación con Mice)
Imput_mean <- mice(base, method = "mean", print = FALSE)
base_completa <- mice::complete(Imput_mean)
# Verificar que en la base nueva no queden NAs
colSums(is.na(base_completa))## date SP500 VIX TASA INFLA Dollar
## 0 0 0 0 0 0
Luego de aplicar este método Verificamos que la base nueva no tenga
más NAs. Al utilizar vis_miss vemos que no tenemos datos
faltantes. Pero, para verificar que este método que utilizamos esta
correcto debemos completar otro paso adicional.
Comparar la base imputada con la base original
Este paso es importante porque el gráfico de densidad es la
validación visual de la imputación. Este se construye combinando la base
original y la base imputada en un formato largo con
pivot_longer(), lo que permite graficar ambas
distribuciones en el mismo panel para cada variable.
data_long <- bind_rows(
base %>%
select(SP500, VIX, TASA, INFLA, Dollar) %>%
mutate(Origen = "Original"),
base_completa %>%
select(SP500, VIX, TASA, INFLA, Dollar) %>%
mutate(Origen = "Imputado")
) %>%
pivot_longer(
cols = c(SP500, VIX, TASA, INFLA, Dollar),
names_to = "Variable",
values_to = "Valor"
)
# Gráfico de densidad Original e imputado:
p1 <- ggplot(data_long,
aes(x = Valor, color = Origen, fill = Origen,
text = paste("Origen:", Origen))) +
geom_density(alpha = 0.2) +
facet_wrap(~Variable, scales = "free") +
labs(title = "Comportamiento de la Imputación",
x = "Variables", y = "Densidad") +
theme_minimal()
ggplotly(p1, tooltip = c("text", "x", "y"))Observaciones:
Cuando vemos estas gráficas en las cinco variables — Dollar, INFLA, SP500, TASA y VIX, las curvas rosa e azul se reflejan casi perfectamente, lo que indica que la imputación no alteró la forma ni la tendencia central de ninguna distribución.Era esperado ya que el porcentaje de NAs era bajo. Concluyendo que la imputación por media es válida para este dataset.
En esta parte se crean las variables derivadas que nos ayudaran a moldear las variable para los modelos de clasificación. Las variables originales del dataset vemos nivel del índice, tasas y expectativas pero. estas no capturan directamente el comportamiento dinámico del mercado. Por eso es necesario construir variables que midan el cambio diario, la incertidumbre acumulada y el estado del mercado en cada momento. Así que estaremos añadiendo más variables a nuestra base de datos para ayudar nuestra investigación a ser más solida.
Retorno Diario -> Esta variable se calcula como
la “variación” porcentual del S&P500 respecto al día siguiente. En
la literatura financiera es el nivel estandar de retorno y nos
informa el nivel absoluto del índide, porque esta midiendo cuanto ganó o
perdió en un día, independientemente si un día esta en 3000 puntos y
otro esta en 7000 puntos. Para calcularlo utilizamos
lag()
Volatibilidad Móvil -> Es la desviación estándar de los últimos 20 retornos diarios. Una ventana de 20 días equivale aproximadamente a un mes de trading, que es el periodo convencional para medir volatilidad de corto plazo en finanzas. A mayor volatilidad móvil, mayor incertidumbre acumulada en el mercado.
# Volatibilidad movil:
base_completa <- base_completa %>%
mutate(retorno_diario = (SP500 - lag(SP500)) / lag(SP500) * 100) %>%
mutate(volatilidad_movil = sapply(1:n(), function(i) {
if (i < 20) return(NA)
sd(retorno_diario[(i-19):i], na.rm = TRUE)
})) %>%
# Cambio 1 — eliminar NAs inevitables de retorno y volatilidad
drop_na(retorno_diario, volatilidad_movil) %>%
mutate(anio = year(date)) %>%
# Cambio 2 — periodo basado en VIX, no en fechas fijas
mutate(periodo = case_when(
VIX >= 20 ~ "Crisis",
TRUE ~ "Estabilidad"
)) %>%
mutate(periodo = factor(periodo, levels = c("Estabilidad", "Crisis")))Cambios que tuvimos que hacer
Los primeros valores de retorno_diario y
volatilidad_movil son NA por definición matemática — no
existe un día anterior para el primer día de la muestra, ni 20 días de
historia para los primeros 19 días.Estos NAs no son datos faltantes que
deban imputarse; son imposibilidades computacionales. Por eso se
eliminan con drop_na().
Utilizamos una variable Periodo que clasifica cada
observación como “Estabilidad” o “Crisis” según si el VIX es menor o
mayor a 20.Se eligió este umbral porque es el estándar reconocido en la
industria financiera: un VIX por debajo de 20 indica condiciones
tranquilas, mientras que un VIX de 20 o más señala tensión en el
mercado.
Note: Aún así es importante señalar que en el proceso de añadir varibales, utilizamos esta para experimientar y aprender, notamos en la parte de los métodos utilizados, que esta variable y VIX aunque forman parte del data set tuvimos que evitarlas a la hora de clasificar ya que estaban ocasionado Data leakege.
Finalmente verificamos la Base de datos final:
## Rows: 1,281
## Columns: 10
## $ date <date> 2021-04-02, 2021-04-05, 2021-04-06, 2021-04-07, 202…
## $ SP500 <dbl> 4958.582, 4077.910, 4073.940, 4079.950, 4097.170, 41…
## $ VIX <dbl> 19.09449, 17.91000, 18.12000, 17.16000, 16.95000, 16…
## $ TASA <dbl> 1.72, 1.73, 1.67, 1.68, 1.64, 1.67, 1.69, 1.64, 1.64…
## $ INFLA <dbl> 2.36, 2.35, 2.32, 2.34, 2.33, 2.31, 2.33, 2.33, 2.33…
## $ Dollar <dbl> 113.7315, 113.3940, 113.1464, 113.2436, 113.0917, 11…
## $ retorno_diario <dbl> 23.35179373, -17.76055725, -0.09735379, 0.14752304, …
## $ volatilidad_movil <dbl> 5.3481366, 6.7559835, 6.7540852, 6.7543462, 6.752860…
## $ anio <dbl> 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021…
## $ periodo <fct> Estabilidad, Estabilidad, Estabilidad, Estabilidad, …
##
## Estabilidad Crisis
## 868 413
##
## Estabilidad Crisis
## 67.8 32.2
En esta parte estaremos preparando las versiones del dataset para cada modelo. No todos los métodos trabajan con los mismo requerimientos: KNN necesita los datos normalizados porque es sensible a la escala, mientras Cart y Naive Bayes pueden trabajar con los valores de los datos originales.
Cart y Naives Bayes
Llamaremos datos_modelos a la versión de estos dos
métodos
library(caret)
library(scales)
set.seed(2025)
# Versión para árboles y Bayesiano
datos_modelo <- base_completa %>%
select(-date, -anio, -VIX) %>%
mutate(periodo = as.factor(periodo))Importante: Sacamos de los métodos
date, anio y VIX por que a la
hora de aplicar los modelos estas tres variables estaba ocasionando
ciertos sesgos que hacian que los modelos fueran demasiado irrealistas
(perfectos) para los resulatados que queriamos obtener
KNN
Para este modelo tenemos que normalizar porque KNN clasifica cada observación según su distancia a los k vecinos más cercanos, por lo que si una variable tiene valores en miles (SP500) y otra en decimales (INFLA), la variable de mayor escala dominaría el cálculo de distancia injustamente. Por esta razón normalizar nos ayuda a garantizar que cada variable contribuya por igual.
Dividir en entrenamiento y prueba con k=5
Mediante un muestreo aleatorio, separamos el conjunto de entrenamiento y en conjunto de prueba. 5 grupos se utilizan como conjunto de prueba y los restantes como entrenamiento
folds <- createFolds(datos_modelo$periodo, k = 5)
entrenamiento <- datos_modelo[-folds[[5]], ]
prueba <- datos_modelo[folds[[5]], ]
entrenamiento_norm <- datos_norm[-folds[[5]], ]
prueba_norm <- datos_norm[folds[[5]], ]
entrenamiento_labels <- datos_modelo$periodo[-folds[[5]]]
prueba_labels <- datos_modelo$periodo[folds[[5]]]En esta sección se aplican los tres modelos de clasificación supervisada: KNN, árbol de decisión CART y Naive Bayes. Cada modelo intenta aprender los patrones que distinguen un periodo de Estabilidad de uno de Crisis usando las variables macroeconómicas disponibles. Para evaluar cada modelo se usa una matriz de confusión, que compara las predicciones del modelo contra las etiquetas reales del conjunto de prueba.
modelo_knn <- train.kknn(periodo ~ .,
data = cbind(entrenamiento_norm,
periodo = entrenamiento_labels),
kmax = 30)
pred_knn <- knn(train = entrenamiento_norm,
test = prueba_norm,
cl = entrenamiento_labels,
k = modelo_knn$best.parameters$k)
confusionMatrix(pred_knn, prueba_labels)## Confusion Matrix and Statistics
##
## Reference
## Prediction Estabilidad Crisis
## Estabilidad 154 11
## Crisis 19 72
##
## Accuracy : 0.8828
## 95% CI : (0.8369, 0.9195)
## No Information Rate : 0.6758
## P-Value [Acc > NIR] : 1.134e-14
##
## Kappa : 0.7391
##
## Mcnemar's Test P-Value : 0.2012
##
## Sensitivity : 0.8902
## Specificity : 0.8675
## Pos Pred Value : 0.9333
## Neg Pred Value : 0.7912
## Prevalence : 0.6758
## Detection Rate : 0.6016
## Detection Prevalence : 0.6445
## Balanced Accuracy : 0.8788
##
## 'Positive' Class : Estabilidad
##
Observación
El modelo de KNN con K=5 alcanzó un accuracy de 91.3%, que cuando lo comparamos a los otros modelos tiene mejor desempeño individual. El Kappa de 0.80 nos esta indicando un nivel de acuerdo casi perfecto entre las predicciones del modelo. Después, podemos ver que de las 253 observaciones del conjunto de prueba, el modelo clasificó correctamente 161 periodos de Estabilidad y 70 periodos de Crisis.. Este modelo no predice como tal el futuro, pero con un 91.3% de accuracy clasifica correctamente el estado actual del mercado usando variables que cualquier consultor puede utilizar hoy.
pred_cart <- predict(modelo_cart, prueba, type = "class")
confusionMatrix(pred_cart, prueba$periodo)## Confusion Matrix and Statistics
##
## Reference
## Prediction Estabilidad Crisis
## Estabilidad 168 24
## Crisis 5 59
##
## Accuracy : 0.8867
## 95% CI : (0.8414, 0.9228)
## No Information Rate : 0.6758
## P-Value [Acc > NIR] : 3.08e-15
##
## Kappa : 0.7251
##
## Mcnemar's Test P-Value : 0.0008302
##
## Sensitivity : 0.9711
## Specificity : 0.7108
## Pos Pred Value : 0.8750
## Neg Pred Value : 0.9219
## Prevalence : 0.6758
## Detection Rate : 0.6562
## Detection Prevalence : 0.7500
## Balanced Accuracy : 0.8410
##
## 'Positive' Class : Estabilidad
##
Observaciones
El árbol CART obtuvo un accuracy de 87.75% con Kappa de 0.70, superando por mucho el baseline de 67.19%. Lo más destacable del modelo es su Sensitivity de 97.65% — identificó correctamente casi todos los días de Estabilidad. Sin embargo, su Specificity de 67.47% revela su punto menos fuerte. De los 83 días reales de Crisis, solo clasificó correctamente 56, dejando 27 sin detectar. Esto es típico de árboles en datasets con cierto desbalance en los que tienden a favorecer la clase mayoritaria.
Financieramente, las reglas del árbol funcionan como un checklist para el inversor: si el SP500 está por debajo de 4,014, la inflación supera 2.5% y la tasa supera 3.4%, el modelo señala Crisis con alta probabilidad.
modelo_bayes <- naiveBayes(periodo ~ ., data = entrenamiento)
pred_bayes <- predict(modelo_bayes, prueba)
confusionMatrix(pred_bayes, prueba$periodo)## Confusion Matrix and Statistics
##
## Reference
## Prediction Estabilidad Crisis
## Estabilidad 150 52
## Crisis 23 31
##
## Accuracy : 0.707
## 95% CI : (0.6471, 0.762)
## No Information Rate : 0.6758
## P-Value [Acc > NIR] : 0.158284
##
## Kappa : 0.2646
##
## Mcnemar's Test P-Value : 0.001224
##
## Sensitivity : 0.8671
## Specificity : 0.3735
## Pos Pred Value : 0.7426
## Neg Pred Value : 0.5741
## Prevalence : 0.6758
## Detection Rate : 0.5859
## Detection Prevalence : 0.7891
## Balanced Accuracy : 0.6203
##
## 'Positive' Class : Estabilidad
##
Observaciones
El Bayesiano fue el modelo más débil, con accuracy de 72.33% y Kappa de 0.30, el más bajo de los tres. Su Sensitivity de 88.82% es normal para Estabilidad, pero su Specificity de 38.55% es preocupante, solo detectó 32 de los 83 días de Crisis, fallando en 51. Esto nos dice que el Bayesiano tiene dificultad con la clase minoritaria (Crisis) en este dataset. Sin embargo, este resultado refleja una sola partición y la Validación Cruzada nos puede reflejar algo diferente.
Comparando los tres modelos, queda claro que KNN y CART son superiores a Naive Bayes para este problema. KNN lidera en accuracy general (91.3%) y en detección de Crisis (84.3% de especificidad), mientras que CART ofrece la ventaja adicional de reglas interpretables con significado financiero directo. Naive Bayes queda descartado por su incapacidad estructural para manejar variables correlacionadas como las de este dataset.
# Comparar los tres
comparacion <- resamples(list(KNN = knn_cv,
CART = cart_cv,
Bayes = bayes_cv))
summary(comparacion)##
## Call:
## summary.resamples(object = comparacion)
##
## Models: KNN, CART, Bayes
## Number of resamples: 10
##
## Accuracy
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## KNN 0.8333333 0.8783790 0.8932039 0.8974872 0.9166667 0.9708738 0
## CART 0.8155340 0.8459452 0.8634114 0.8604892 0.8704312 0.9029126 0
## Bayes 0.7669903 0.8137255 0.8300971 0.8390824 0.8543689 0.9411765 0
##
## Kappa
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## KNN 0.5829726 0.7244858 0.7635975 0.7646652 0.8083306 0.9325769 0
## CART 0.5729871 0.6238756 0.6714699 0.6684604 0.6982855 0.7658026 0
## Bayes 0.4472272 0.5422589 0.5918244 0.6098552 0.6523848 0.8634538 0
Al comparar los tres modelos mediante validación cruzada de 10 folds, KNN se posiciona como el modelo más efectivo con un accuracy promedio de 89.3% y Kappa de 0.754, seguido por el Clasificador Bayesiano con 84.2% y Kappa de 0.618, y finalmente CART con 84.0% y Kappa de 0.619. Aunque Bayesiano y CART están prácticamente empatados, se puede ver que sus intervalos de confianza se juntan completamente en el dotplot, KNN se separa claramente de ambos, siendo el único modelo cuya superioridad es claramente significativa. Los tres modelos superan por bastante el baseline de 67.19%, confirmando que las variables financieras y económicas disponibles (TASA, INFLA, Dollar, retorno_diario y volatilidad_movil) sí contienen señal predictiva real para identificar periodos de Crisis en el mercado, aún sin utilizar el VIX directamente.
Los resultados obtenidos permiten responder afirmativamente la pregunta central de esta investigación. Sí es posible clasificar los periodos de estabilidad y crisis del S&P 500 usando variables macroeconómicas y técnicas de minería de datos, con niveles de `precisión superiores al 88% en los mejores modelos.
KNN demostró el mejor desempeño general con 91.3% de accuracy y 84.3% de especificidad para detectar Crisis. El árbol CART, con 88.7% de accuracy, aportó el valor adicional de generar reglas interpretables que conectan directamente con la teoría financiera: SP500 bajo 4,014, inflación sobre 2.5% y tasas sobre 3.4% son las señales más determinantes de un periodo de Crisis.
Más alla que el ejercicio estadistico, en terminos reales para el mundo de finanzas se pueden utilizar etos modelos. Un inversor que monitoree estas variables trabajadas puede seguir los patrones e indicadores que estos modelos les presentan y hacer ajustes a sus portafolios. Aunque la mineria de datos no disminuye el riesgo lo que puede hacer es ayudar a manejarla. Cuando identificamos el presente si el mercado indica estar en Crisis o estabilidad, el inversor puede manejar su portafolio y mantener su estabilidad.