Seguimos con la serie sobre Machine Learning aplicada a mercados financieros. En esta nueva entrega os vamos a enseñar a utilizar el algoritmo de k-Nearest Neighbors (abreviado KNN) o k-Vecinos Más Cercanos.

La idea detrás de este algoritmo de aprendizaje supervisado es sencilla: de la misma manera que en un partido de fútbol los seguidores del mismo equipo tratan de sentarse cerca dentro de una misma zona, el algoritmo KNN trata de clasificar cada dato nuevo en el grupo que le corresponde en base a la presencia de los k vecinos más cercanos de un tipo u otro.

Para ello, calcula la distancia en el plano del elemento nuevo con respecto a cada uno de los existentes, y ordena dichas distancias de menor a mayor para ir seleccionando el grupo al que pertenece. Por tanto, el grupo asignado será, por tanto, el de mayor frecuencia con menores distancias.

Seguramente después de leer la descripción anterior del algoritmo de KNN algunos de nuestros lectores se estén tirando de los pelos pensando "¿cómo no se me ocurrió a mí antes?". Y es que se trata de un algoritmo muy simple pero a priori bastante efectivo (como todo lo bueno de esta vida ;)).


Manos a la Obra
En esta ocasión vamos a trabajar con los rendimientos porcentuales del S&P500, así como otros datos. Podemos descargarlos directamente usando la librería ISLR, que contiene una colección de datos para hacer experimentos del libro An Introduction to Statistical Learning: with Applications in R (podéis ver el listado completo de datos incluidos en el paquete aquí: http://www-bcf.usc.edu/~gareth/ISL/data.html); asimismo necesitaremos la librería class que contiene la función knn y la librería dplyr:

install.packages(c(“ISLR”,"class", “dplyr”))
library(ISLR)
library(class)
library(dplyr)

El set de datos Smarket incluido en ISLR contiene los rendimientos del S&P500 para los años comprendidos entre 2001 y 2005 (columna Today), diferentes retardos de los rendimientos (Lag1 a Lag5), el volumen negociado (Volume) y etiquetas de texto que identifican la dirección del mercado en la sesión (Direction), tal y como podéis ver en la siguiente imagen:

Cabecera de Datos Smarket

El objetivo de este ejemplo es tratar de buscar qué características previas definen a un día alcista y cuáles a un día bajista usando KNN, de tal forma que con la información de que disponemos en el momento t-1 podamos prever que sucederá al día siguiente en el mercado.

Para ello, debemos obtener las reglas previamente creando un conjunto de datos para entrenar al algoritmo, validando posteriormente dichas reglas realizando predicciones fuera de muestra. Para ello, utilizamos el siguiente código:

train_Market = Smarket %>%
  filter(Year < 2005) %>%
  select(Lag1, Lag2, Lag3, Volume)

train_Direction = Smarket %>%
  filter(Year < 2005) %>%
  select(Direction) %>%
  .$Direction

test_Market = Smarket %>%
  filter(Year >= 2005)%>%
  select(Lag1, Lag2, Lag3, Volume)


Con estas líneas lo que hemos hecho es tomar como conjunto de entrenamiento (definido por el input train_Market y el output train_Direction) a la muestra comprendida entre los años 2001 y 2004, Como variables para definir los vecinos hemos tomado los retardos 1 a 3 y el volumen negociado en la sesión anterior. Finalmente, como conjunto de validación tenemos los inputs contenidos en test_Market. mientras que para el conjunto de validación vamos a usar el año 2005.

Seguidamente ejecutamos la función de predicción de KNN. Previamente a su ejecución debemos crear una semilla que se usará para clasificar de forma aleatoria a los vecinos en caso de empate en la puntuación.

set.seed(1)
knn_pred = knn(train_Market, test_Market, train_Direction, k = 1)

Observad que en la función hemos tomado k=1 (esto es, solo consideramos un vecino para clasificar), por lo que es de esperar que el resultado que obtengamos no será muy bueno.

Pasamos a comparar las predicciones realizadas con los valores verdaderos de Direction en el año 2005. Para ello simplemente extraemos de la muestra la parte de Direction correspondiente a ese año y creamos una matriz de confusión para comparar aciertos:

test_Direction = Smarket %>%
  filter(Year >= 2005) %>%
  select(Direction) %>%
  .$Direction

table(knn_pred, test_Direction)
mean(knn_pred == test_Direction)


Los resultados que se obtienen son los siguientes:

Resultados KNN

Como podéis ver, nos sale casi mejor tirar una moneda para acertar si el día siguiente será alcista o bajista por cuanto tan solo clasificamos correctamente el 42% de los días en el conjunto de validación.

Veamos qué sucede si variamos el valor de k. Para ello nos construimos este bucle en R, con el que repetimos las predicciones variando el valor de k entre 1 y 10:

accuracy <- rep(0, 10)
k <- 1:10
for(x in k){
  prediction <- knn(train_Market, test_Market, train_Direction, k = x)
  test_Direction = Smarket %>%
    filter(Year >= 2005) %>%
    select(Direction) %>%
    .$Direction
    accuracy[x] <- mean(prediction == test_Direction)
}

Finalmente representamos un gráfico comparando el valor de k con el porcentaje de acierto obtenido en el conjunto de validación mediante esta línea de código:

plot(k, accuracy, type = 'b') 

El resultado es el siguiente:

Comparativa de K vs Precisión

Como podéis ver el resultado no es nada espectacular: tan solo en el caso k=10 superamos ligeramente el 50% (en concreto un 51.59%), sin lugar a dudas un resultado bastante desalentador.


Problemas con los Vecinos
Después de la simulación anterior, nuevamente nos encontramos con que esta técnica no parece muy apropiada para modelizar el comportamiento de los mercados. El problema fundamentalmente con el algoritmo KNN es que es muy sensible a variaciones en sus parámetros; en particular:

  • Diferentes valores del número de vecinos utilizado (k) pueden conducir a resultados muy distintos. Generalmente la elección de este parámetro se realiza tras realizar algunas pruebas con diferentes valores (de manera similar a lo que hemos hecho en el ejemplo).
  • La métrica de similitud utilizada para medir las distancias entre vecinos también influye notablemente, de tal forma que si variamos los pesos de cada variable también cambiarán los resultados.
  • Asimismo, se trata de un algoritmo lento por cuanto cada instancia de prueba es comparada contra todos los datos contenidos en el conjunto de entrenamiento.

Generalmente el algoritmo KNN se utiliza sobre todo para tareas de recomendación en web y análisis de hábitos de los usuarios, situaciones en las que a priori parece más sencillo reconocer pautas por afinidad. Sin embargo, ese no parece ser el caso de los mercados financieros así que… ¡seguiremos profundizando en el apasionante mundo del Machine Learning!

 

Saludos,
X-Trader