Continuamos con la serie sobre técnicas de Machine Learning aplicadas al trading. En esta ocasión iniciamos el recorrido por los métodos de aprendizaje supervisado, comenzando por las regresiones contraídas o regresiones Ridge y las regresiones Lasso.


Antes de nada… ¡una de datos, por favor!
Lógicamente antes de ponernos a jugar con estas técnicas necesitamos descargar datos para jugar. Para ello vamos a aprovechar la potente librería Quantmod que tenemos para R con la que podremos descargar multitud de datos históricos.
Para instalarla y cargarla tan sencillo como ejecutar estas dos líneas de código:

install.packages("quantmod")
library(quantmod)


Ahora toca descargar alguna serie para hacer nuestros experimentos con Machine Learning. Para este artículo he seleccionado las acciones de Cisco entre 2006 y 2017. Para descargarlo desde Yahoo! Finance simplemente ejecutamos esta línea:

CSCO <- getSymbols("CSCO", src="/yahoo", from = "2006-01-01", to = "2017-12-31", auto.assign = FALSE)

En el ejemplo que os voy a proponer hoy vamos a plantear el supuesto de que el valor de un conjunto de medias móviles simples de 20, 100 y 200 períodos (inputs) tiene capacidad para predecir el cierre de la siguiente vela. Para ello, necesitamos calcular tres inputs (medias móviles) y un output (cierre de la vela siguiente).

Realizamos los cálculos necesarios:

MA20 <- rollmean(CSCO[,4], k = 20, fill=NA, align="right")
MA100 <- rollmean(CSCO[,4], k = 100, fill=NA, align="right")
MA200 <- rollmean(CSCO[,4], k = 200, fill=NA, align="right")
Cl <- CSCO[,4]

Eliminamos los primeros 200 valores para limpiar NAs

MA20<-MA20[-1:-200,]
MA100<-MA100[-1:-200,]
MA200<-MA200[-1:-200,]
Cl<-Cl[-1:-200,]

Finalmente obtenemos los inputs quitando el último valor de la serie, y el output quitando el primer valor de la serie:

MA20 <- MA20[-nrow(MA20)]
MA100 <- MA100[-nrow(MA100)]
MA200 <- MA200[-nrow(MA200)]
Cl <- Cl[-1]

Con esto ya podemos empezar a divertirnos un poco ;).

Regresión Ridge
La regresión Ridge surge como una solución al problema que se plantea cuando queremos estimar una relación por Mínimos Cuadrados Ordinarios y hace acto de presencia la multicolinealidad.

Hagamos un repaso rápido: supongamos que queremos obtener los valores para los parámetros de una ecuación como la siguiente:

Y = b0+b1X1+b2X2+…+bnXn+e

Donde bi son los parámetros del modelo, Xi las variables independientes (inputs), Y la variable dependiente (output) y e es un término de error.

Para estimar esa relación, hay que realizar el siguiente cálculo matricial:

(X’X)-1 · X’Y

Pero ¿qué ocurre si hay multicolinealidad, esto es, si dos o más variables son combinación lineal entre sí? Pues que sencillamente X’X no es invertible y no es posible realizar el cálculo. En el mejor de los casos, tendremos un determinante de la matriz X’X que tenderá a cero y disparará los valores de los parámetros estimados.

Aquí es donde entra en juego la regresión Ridge, que plantea minimizar la suma del cuadrado de los errores usando estimadores contraídos, los cuales se obtienen modificando la expresión anterior:

(X’X+λI)-1 · X’Y

Es decir, se introduce un parámetro Lambda (denominado término de contracción o shrinkage term) que frena el crecimiento explosivo de los parámetros que se produce en presencia de multicolinealidad. Modulando Lambda podemos generar variaciones de esta regresión, siendo la estimación por Mínimos Cuadrados un caso particular de regresión contraída (cuando λ=0).

El uso de la regresión Ridge no es precisamente la panacea ya que al usarla obtendremos estimadores sesgados aunque su varianza será mínima. Por tanto, la gracia del asunto estará en encontrar el valor de Lambda que más contrae las estimaciones.  Para ello, se utiliza el criterio generalizado de validación cruzada, mediante el que se fija un rango de posibles valores para lambda y se calcula la validación cruzada. El lambda óptimo será aquel que minimice la validación cruzada.

Bien pasemos manos a la obra: primero vamos a estimar la relación entre las medias y el comportamiento futuro de las velas futuras mediante Mínimos Cuadrados Ordinarios. Siguiendo con las variables calculadas anteriormente, ejecutamos el siguiente comando para realizar la estimación y mostramos resultados (obsérvese que he quitado el término constante del modelo con ese -1 al final de la ecuación, no salía significativo):

EstMCO <- lm(Cl ~ MA20+MA100+MA200 -1)
summary(EstMCO)

Obtenemos lo siguiente:

Call: lm(formula = Cl ~ MA20 + MA100 + MA200 - 1)

Residuals:
    Min      1Q  Median      3Q     Max
-4.8282 -0.5513  0.0636  0.6316  3.3491

Coefficients:
      Estimate Std. Error t value Pr(>|t|)    
MA20   1.14322    0.01215  94.085  < 2e-16 ***
MA100 -0.21881    0.02196  -9.962  < 2e-16 ***
MA200  0.07713    0.01452   5.311 1.18e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.9875 on 2816 degrees of freedom
Multiple R-squared:  0.9984,    Adjusted R-squared:  0.9984
F-statistic: 5.929e+05 on 3 and 2816 DF,  p-value: < 2.2e-16

Podéis observar que al incluir ese -1 en la fórmula hemos eliminado el término constante de la regresión ya que en la primera prueba salió que no era estadísticamente significativa.

Hemos dicho antes que la regresión Ridge se usa para resolver el problema de la multicolinealidad, algo que muy probablemente esté pasando en este caso por cuando las medias móviles están altamente correlacionadas entre sí. Afortunadamente para detectar si existe ese problema en el modelo podemos hacer un test denominado Variance-Inflation Factor con la función VIF que viene en la librería Car. En nuestro ejemplo, ejecutaríamos:

vif(EstMCO)

Obteniendo lo siguiente:


    MA20    MA100    MA200
261.4007 836.1191 357.3335

La interpretación del test es sencilla: si alguno de los valores asociados a las variables es superior a 2 entonces estaremos ante la presencia de multicolinealidad. En nuestro caso, no es que tengamos multicolinealidad, es que tenemos ¡¡¡una barbaridad de ella!!! Así que ha llegado el momento de hacer una regresión Ridge.  

Para ello primero debemos instalar y cargar los siguientes paquetes: glmnet, ISLR, dply y tidyr.

install.packages("glmnet")
install.packages("ISLR")
install.packages("dplyr")
install.packages("tidyr")
library(glmnet)
library(ISLR)
library(dplyr)
library(tidyr)

A continuación definimos el modelo a estimar:

x = model.matrix(~MA20+MA100+MA200)
y = Cl

Lo primero es determinar el valor óptimo de lambda. Para ello, glmnet cuenta con una función denominada cv.glmnet que nos permite encontrar el valor que minimiza el error cuadrático medio ediante validación cruzada. En la siguiente línea de código os mostramos cómo usar la función, tened en cuenta que Alpha=0 indica que estamos trabajando con una regresión Ridge (los diferentes valores de Alpha indican el método que estamos usando):

cv.out = cv.glmnet(x, y, alpha = 0)
bestlambda = cv.out$lambda.min  
bestlambda

En nuestro ejemplo obtenemos lambda = 0.5479145. Con este valor ya podemos estimar el modelo usando regresión Ridge:

EstRidge = glmnet(x, y, alpha = 0, lambda = bestlambda)

Finalmente obtenemos los valores de los parámetros del modelo:

coef(EstRidge)

El resultado es el siguiente:

                    s0
(Intercept) 0.71710919
MA20        0.67555417
MA100       0.23767308
MA200       0.06239834

Como podemos ver los valores son notablemente distintos de los obtenidos por MCO.

En general, la regresión Ridge nos va a resultar útil es casos en los que tratemos de valorar la capacidad predictiva de un conjunto de indicadores y nos encontremos con que algunos de ellos solapan información pero, a pesar de ello, deseemos mantenerlos en el modelo. Sin embargo, con la regresión Ridge tenemos un problema: no es capaz de decidir qué variables sobran en el modelo ya que, aunque contrae los valores de los parámetros, nunca llega a asignar un valor cero a ningún parámetro. Pero tranquilos que tenemos la solución para ello: se trata de la regresión Lasso.


Regresión Lasso

El método Lasso surge como alternativa al ajuste por regresión Ridge para superar la principal desventaja de este último método: su incapacidad para excluir predictores del modelo. Es decir, posee la capacidad de fijar los valores de los parámetros exactamente a cero, lo que permite además de reducir la varianza, realizar selección de predictores.

Aprovechando todo el código que hemos usado podemos realizar la estimación mediante Lasso del modelo anterior, simplemente repitiendo los mismos pasos pero poniendo Alpha=1 en la función glmnet. Así pues, reescribimos el código variando Alpha tal que:

cv2.out = cv.glmnet(x, y, alpha = 1)
bestlambdaLasso = cv2.out$lambda.min  
bestlambdaLasso

El valor de lambda ahora es de 0.002924055. Con este valor ya podemos estimar el modelo usando regresión Ridge:

EstLasso = glmnet(x, y, alpha = 1, lambda = bestlambdaLasso)

Finalmente obtenemos los valores de los parámetros del modelo:

coef(EstLasso)

El resultado ahora es el siguiente:

                     s0
(Intercept)  0.18503575
MA20         1.12007704
MA100       -0.16897900
MA200        0.04279963

Como podemos ver, hemos obtenido valores muy diferentes de los que ofrecía la regresión Ridge. Esta alta variabilidad de los resultados en este tipo de regresiones y el hecho de que los resultados están condicionados al valor de lambda, hacen en mi opinión que las predicciones obtenidas con este tipo de regresiones no sean demasiado precisas.

Conclusión
Nuestro primer recorrido por las técnicas de machine learning posiblemente les haya parecido algo decepcionante pero quizás conviene recordar algo que venimos advirtiendo de manera implícita en los primeros artículos de la serie: cada técnica de machine learning tiene un cometido concreto y en el caso que hemos visto hoy con las regresiones de tipo Ridge y Lasso simplemente resolvemos un problema concreto, el de la multicolinealidad. Por tanto, salvo que se encuentren con un problema como el que hemos sugerido hoy (uso de inputs muy correlacionados que solapan información) realmente usar este tipo de regresiones en otros contextos no tiene mucho sentido. No obstante, si en algún momento van a trabajar con estas regresiones deben tener en cuenta que:

  • La regresión Lasso, al hacer selección de variables, generalmente es considerado mejor que la regresión Ridge, si bien no hay una regla para confirmar que un método domina a otro.
  • La regresión Lasso suele funcionar mejor en entornos con un bajo número de predictores con elevados valores para sus parámetros, siendo el resto muy pequeños o cero.
  • Por su parte, la regresión Ridge funciona mejor cuando el output es una función de muchos factores predictivos, todos con parámetros de tamaño similar.

En cualquier caso, ¡que no cunda el pánico! En las próximas entregas veremos técnicas mucho más eficientes y potentes que estoy seguro que les encantarán.

 

Saludos,
X-Trader