library("quantmod")
getSymbols("^DJI",src="yahoo")
[1] "DJI"
<- DJI[,"DJI.Close"] dji
April 7, 2025
Los modelos de machine learning hacen predicciones que caen en cuatro resultados posibles:
Verdadero positivo (True Positive): El modelo predice correctamente la clase positiva.
Falso positivo (False Positive): El modelo predice incorrectamente la clase positiva cuando el resultado real es negativo, también conocido como error de tipo I.
Verdadero negativo (True Negative): El modelo predice correctamente la clase negativa.
Falso negativo (False Negative): El modelo predice incorrectamente la clase negativa cuando el resultado real es positivo, también conocido como error de tipo II.
Estos resultados forman la base para evaluar el rendimiento de un modelo.
La presencia de falsos positivos y falsos negativos resalta dónde las predicciones del modelo fallan, o, en palabras más sencillas, dónde se confunden los valores. Por eso, la tabla 2x2 utilizada para organizar estos resultados ha ganado el impresionante título de matriz de confusión
.
Lo que realmente es notable es que, a pesar de contener solo cuatro números, una matriz de confusión puede generar al menos 27 métricas distintas que miden el poder predictivo. Cada métrica tiene un propósito único, dependiendo del contexto en el que se aplique. Dado el gran número de métricas, no puedo incluirlas todas en un solo post, así que a lo largo de esta serie las veremos. En cada post, exploraré un grupo de métricas relacionadas, explicando qué significan, demostrando cómo calcularlas en R y proporcionando ejemplos del mundo real de dónde y por qué son importantes.
Ahora, sumérjamonos y creemos una matriz de confusión a partir de las predicciones del modelo.
Para construir una matriz de confusión para un modelo de *machine learning*, sigue estos pasos:
Divide tu conjunto de datos en dos partes, aproximadamente un 80%
para entrenamiento y un 20%
para prueba.
Usa solo los datos de entrenamiento para enseñar
al modelo.
Usa el modelo entrenado para predecir
las probabilidades del resultado positivo para los datos de prueba.
Luego, convierte las probabilidades en categorías, es decir, si la probabilidad es mayor a 0.5
, clasifícala como “sí”, y si es 0.5 o menor,
clasifícala como “no”.
Finalmente, compara los valores reales en los datos de prueba con los predichos utilizando una sencilla tabla 2x2 llamada matriz de confusión
.
Aquí vamos a usar datos de stocks financieros, vamos a construir un modelo de predicción que nos indique la dirección del mercado. La dirección del mercado es muy importante para los inversores o traders. Predecir la dirección del mercado es una tarea bastante desafiante, ya que los datos del mercado incluyen mucho ruido. El mercado se mueve hacia arriba o hacia abajo, y la naturaleza del movimiento del mercado es binario. Un modelo de regresión logística nos ayuda a ajustar un modelo usando comportamiento binario y predecir la dirección del mercado. La regresión logística es uno de los modelos probabilísticos que asigna una probabilidad a cada evento.
Lo primero es conseguir la información de algún índice bursátil, digamos DJI (Dow Jones Industrial), que refleja el comportamiento del precio de la acción de las treinta compañías industriales más importantes y representativas de Estados Unidos. Para esto usamos la librería quantmod
, y extraemos el precio de cierre:
Vamos a agreagar al modelo más variables: el promedio de los 10 días anteriores, el promedio de 20 días anteriores y sus desviaciones estándar, el RSI
, el MACD
y las Bollinger Bands
. Esas variables harán parte de las variables independientes del modelo, la dirección será nuestra variable dependiente, la que queremos predecir.
Ahora verificamos si hubo cambio en la dirección, y armamos el conjunto de datos completo. Haz una pausa en el código, fíjate que nuestra decisión depende de la evaluación respecto del valor 20 días atrás:
Ahora, para implementar regresión logística
, debemos dividir los datos en dos partes. La primera parte son los datos en muestra y la segunda parte son los datos fuera de muestra.
Los datos en muestra se utilizan para el proceso de construcción del modelo y los datos fuera de muestra se utilizan para fines de evaluación. Este proceso también ayuda a controlar la varianza y el sesgo en el modelo. Las siguientes cuatro líneas corresponden a las fechas de inicio y fin de los datos en muestra y los datos fuera de muestra, respectivamente.
Los siguientes dos comandos tienen como objetivo obtener el número de fila para las fechas, es decir, la variable isrow
extrae los números de fila para el rango de fechas en muestra y osrow
extrae los números de fila para el rango de fechas fuera de muestra.
Las variables isdji
y osdji
son conjuntos de datos en muestra y fuera de muestra, respectivamente.
Si miras los datos en muestra, es decir, isdji
, te darás cuenta de que la escala de cada columna es diferente: algunos columnas están en una escala de 100, otros están en una escala de 10,000 y algunos otros están en una escala de 1. La diferencia en la escala puede causar problemas en tus resultados, ya que se asignan pesos más altos a variables escaladas más altas. Por lo tanto, antes de seguir adelante, debes considerar estandarizar el conjunto de datos. Voy a utilizar la siguiente fórmula:
\[ \text{Datos estandarizados}=\frac{X - Media(X)}{\text{Desviación Estándar}(X)} \]
Se genera una matriz identidad de dimensión igual a los datos en muestra utilizando el siguiente comando, que se va a utilizar para la normalización.
La línea anterior también estandariza la columna de Dirección
, es decir, la columna última. No queremos que la dirección se estandarice, por lo que reemplazo la columna última nuevamente con la variable dirección para el rango de datos en muestra.
Ahora hemos creado todos los datos necesarios para construir el modelo. Debe construir un modelo de regresión logística y esto nos ayudará a predecir la dirección del mercado basado en los datos en muestra.
En primer lugar, en este paso, creé una fórmula que tiene la dirección como variable dependiente y todas las demás columnas como variables independientes. Luego, utilicé un modelo lineal generalizado, es decir, glm()
, para ajustar un modelo que tiene fórmula, familia y conjunto de datos:
Se puede ver un resumen del modelo utilizando el siguiente comando:
Call:
glm(formula = formula, family = "binomial", data = norm_isdji)
Coefficients: (3 not defined because of singularities)
Estimate Std. Error z value Pr(>|z|)
(Intercept) 1.10262 0.11960 9.219 < 2e-16 ***
DJI.Close 32.85949 5.19330 6.327 2.50e-10 ***
DJI.Close.1 -34.19696 11.07670 -3.087 0.002020 **
DJI.Close.2 1.48406 10.77744 0.138 0.890477
DJI.Close.3 0.06915 0.24833 0.278 0.780654
DJI.Close.4 -0.29325 0.31664 -0.926 0.354366
rsi 0.04495 0.15775 0.285 0.775700
rsi.1 -1.35799 0.23593 -5.756 8.62e-09 ***
macd 3.28536 0.83327 3.943 8.06e-05 ***
signal 1.33860 0.35234 3.799 0.000145 ***
macd.1 -0.67900 0.48453 -1.401 0.161105
signal.1 2.04865 0.92596 2.212 0.026935 *
dn NA NA NA NA
mavg NA NA NA NA
up NA NA NA NA
pctB 2.35392 0.25880 9.096 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 4734.2 on 3646 degrees of freedom
Residual deviance: 1218.2 on 3634 degrees of freedom
(33 observations deleted due to missingness)
AIC: 1244.2
Number of Fisher Scoring iterations: 8
Usamos la función predict()
para ajustar valores en el mismo conjunto de datos para estimar el mejor valor ajustado.
Una vez que hayas ajustado los valores, debes intentar convertirlos a probabilidades utilizando el siguiente comando. Esto convertirá la salida en forma probabilística y la salida será en el rango \([0,1]\).
La primera línea del código muestra que dividimos la figura en dos filas y una columna, donde la primera figura es para la predicción del modelo y la segunda figura es para la probabilidad.
Como las probabilidades están en el rango \((0,1)\), así también lo está nuestro vector prob. Ahora, para clasificarlos en una de los dos clases, consideré la dirección hacia arriba \((1)\) cuando prob
es mayor que \(0.5\) y la dirección hacia abajo \((0)\) cuando prob
es menor que \(0.5\). Esta asignación se puede hacer utilizando los siguientes comandos:
Una vez que hemos determinado la dirección predicha, debemos verificar la precisión del modelo: cuánto ha predicho la dirección hacia arriba como hacia arriba y la dirección hacia abajo como hacia abajo. Es posible que haya algunos escenarios en los que predijo lo contrario de lo que es, como predijo hacia abajo cuando en realidad es hacia arriba y viceversa. Puedemos utilizar el paquete caret
para calcular confusionMatrix()
, que devuelve una matriz como salida. Todos los elementos diagonales son predichos correctamente y los elementos fuera de la diagonal son errores o predichos incorrectamente. Debe tener como objetivo reducir los elementos fuera de la diagonal en una matriz de confusión.
library(caret)
matrix<- confusionMatrix(factor(pred_direction),
factor(norm_isdji$Direction),
mode = "everything")
matrix
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 1152 108
1 134 2253
Accuracy : 0.9336
95% CI : (0.9251, 0.9415)
No Information Rate : 0.6474
P-Value [Acc > NIR] : <2e-16
Kappa : 0.854
Mcnemar's Test P-Value : 0.108
Sensitivity : 0.8958
Specificity : 0.9543
Pos Pred Value : 0.9143
Neg Pred Value : 0.9439
Precision : 0.9143
Recall : 0.8958
F1 : 0.9049
Prevalence : 0.3526
Detection Rate : 0.3159
Detection Prevalence : 0.3455
Balanced Accuracy : 0.9250
'Positive' Class : 0
Ten cuidado, sin embargo, porque aunque una matriz de confusión solo tiene cuatro números, es fácil confundirse. Esto es lo que debes recordar: las filas deben representar los valores predichos por tu modelo o datos de prueba
. La fila superior es para predicciones positivas, y la fila inferior es para predicciones negativas. Las columnas deben representar los valores reales o verdaderos, también llamados el "estándar de oro"
. La columna izquierda es para resultados positivos, mientras que la columna derecha es para resultados negativos.
La función epiR::epi.tests()
tiene dos características útiles: te recuerda colocar los resultados reales o los valores reales en las columnas y proporciona intervalos de confianza al \(95\%\), algo que muchas funciones comunes de machine learning como la matriz de confusión del paquete caret::confusionMatrix()
no incluyen. En este post, vemos cómo usar esta función. Dicho esto, la función confusionMatrix
también tiene ventajas. Por ejemplo, incluye métricas como la tasa de detección (Detection Rate
), que es un excelente punto de partida para analizar el rendimiento del modelo porque es simple e intuitiva.
La tabla precedente muestra que hemos logrado una predicción correcta del 93%, ya que 1152 + 2253 = 3405 predicciones son correctas de un total de 3647 (suma de todos los cuatro valores). En general, se considera una buena predicción cualquier valor superior al \(80\%\) en los datos de muestra; sin embargo, el \(80\%\) no es un valor fijo y debe determinarse en función del conjunto de datos y la industria.
Ahora que has implementado el modelo de regresión logística, que ha predicho correctamente el 93%, debes probar su capacidad de generalización. Debe probarse este modelo utilizando datos de muestra y verificar su precisión.
El primer paso es estandarizar los datos de muestra utilizando la fórmula de arriba. En este caso, la media y la desviación estándar deben ser las mismas que las utilizadas para la normalización de los datos de muestra.
A continuación, utilizaremos la función predict()
en los datos de muestra y utilizaremos este valor para calcular la probabilidad.
Una vez que se hayan determinado las probabilidades para los datos de muestra, debes clasificarlos en clases “Arriba” o “Abajo” utilizando los siguientes comandos. La función confusionMatrix()
aquí generará una matriz para los datos de muestra.
ospred<- predict(model,norm_osdji)
osprob<- 1 / (1+exp(-(ospred)))
ospred_direction<- NULL
ospred_direction[osprob> 0.5] <- 1
ospred_direction[osprob<= 0.5] <- 0
osmatrix<- confusionMatrix(factor(ospred_direction),
factor(norm_osdji$Direction))
osmatrix
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 376 37
1 26 482
Accuracy : 0.9316
95% CI : (0.9133, 0.947)
No Information Rate : 0.5635
P-Value [Acc > NIR] : <2e-16
Kappa : 0.8614
Mcnemar's Test P-Value : 0.2077
Sensitivity : 0.9353
Specificity : 0.9287
Pos Pred Value : 0.9104
Neg Pred Value : 0.9488
Prevalence : 0.4365
Detection Rate : 0.4083
Detection Prevalence : 0.4484
Balanced Accuracy : 0.9320
'Positive' Class : 0
Esto muestra un 93% de precisión en los datos de muestra. La calidad de la precisión es más allá del alcance de este post, por lo que no cubriré si la precisión en los datos de muestra es buena o mala y qué técnicas se pueden utilizar para mejorar este rendimiento.
Un modelo de trading realista también tiene en cuenta los costos de trading y la pérdida de mercado, lo que disminuye significativamente las probabilidades de ganar. La próxima tarea es diseñar una estrategia de trading utilizando las direcciones predichas. Exploraré cómo implementar una estrategia de trading automatizada utilizando señales predichas en otro post.
Por ahora, es importante que conozcas que la tasa de detección nos dice la proporción de positivos reales que el modelo identificó correctamente. En nuestro ejemplo, la tasa de detección es del 0.3158761, pero ¿es esto bueno o malo? Depende de la situación. En escenarios de alto riesgo, como la detección de enfermedades o la prevención de fraudes, un 32% probablemente sea demasiado bajo, ya que se pierden muchos verdaderos positivos, lo que puede tener consecuencias graves.
En problemas desafiantes, como aquellos con conjuntos de datos desequilibrados o tareas complejas, un 32% podría representar realmente un progreso sólido. Por ejemplo, la tasa de detección es especialmente útil en fabricación para monitorear las tasas de defectos (piensa en Six Sigma y las partes por millón). Ayuda a identificar productos defectuosos en fábricas modernas operadas por robots, donde incluso mejoras pequeñas pueden marcar una gran diferencia. Sin embargo, la tasa de detección tiene una limitación importante: solo se enfoca en los verdaderos positivos e ignora completamente los falsos positivos. Esto significa que no nos da una visión completa de cómo está funcionando el modelo. Para obtener una comprensión más completa, necesitamos mirar la siguiente métrica importante en la matriz de confusión: la prevalencia de detección (Detection Prevalence
).
La prevalencia de detección, también conocida como prevalencia aparente, es el porcentaje de casos positivos que el modelo predice. En nuestro ejemplo del finanzas, representa la proporción de veces que el modelo predice que el precio de la acción bajará. Así es como la calculamos: sumamos todos los positivos predichos, que son los valores en la fila superior de la matriz de confusión, y los dividimos entre el número total de casos. Esto significa que nuestro modelo predice que el 0.3454894 del precio de la acción bajará. ¿Por qué se llama prevalencia aparente? El término refleja el hecho de que estas son predicciones modeladas que pueden no alinearse con la realidad. Para entender la realidad, necesitamos calcular la prevalencia verdadera, que exploraremos a continuación.
La prevalencia verdadera muestra el porcentaje real de casos positivos, incluyendo aquellos que el modelo se perdió. En el ejemplo de stocks, es la proporción real de subidas de la acción, incluso aquellos a los que el modelo predijo incorrectamente que no subían. Para calcular la prevalencia verdadera, nos enfocamos en la columna izquierda de la matriz de confusión, sumamos los verdaderos positivos y los falsos negativos, y los dividimos entre el tamaño de la población.
Es tan simple como eso. La prevalencia verdadera es del 35.26%, lo que es mayor que la prevalencia aparente predicha por el modelo, que es del 34.55%. Esta es una brecha enorme, especialmente en casos sensibles, los que involucran vidas humanas. Esto nos lleva naturalmente a la sensibilidad, una métrica fundamental para entender cómo bien identifica un modelo los verdaderos positivos.
La sensibilidad es solo el comienzo. Hay siete métricas esenciales de rendimiento que forman la base de la evaluación de cualquier modelo de machine learning. En el próximo post, pasaré por cada una de ellas paso a paso.
@online{chiquito_valencia2025,
author = {Chiquito Valencia, Cristian},
title = {Modelando Stocks Financieros},
date = {2025-04-07},
url = {https://cchiquitovalencia.github.io/posts/2025-04-07-predictions_on_stocks/},
langid = {en}
}