No se deje engañar: predicciones engañosas de precios de criptomonedas usando Deep Learning

Por qué debe tener cuidado con las redes neuronales para el comercio

Así que construí una red neuronal profunda para predecir el precio de Bitcoin, y es asombrosamente precisa.

¿Curioso?

Vea los resultados de predicción para usted.

Parece bastante preciso, ¿no?

Y antes de preguntar: Sí, la evaluación anterior se realizó sobre datos de prueba no vistos; solo se utilizaron datos previos para capacitar al modelo (más detalles más adelante).

¡Así que esta es una máquina de hacer dinero que puedo usar para enriquecerme!

¿Derecha?

De hecho, le doy el código del modelo anterior para que pueda usarlo usted mismo …

Ok, para allí . No lo hagas

Repito: ¡ No lo hagas! No lo use para el comercio.

No te dejes engañar

Hay algo totalmente engañoso sobre estos resultados.

Dejame explicar.

Demasiado bueno para ser verdad

Durante las últimas semanas y meses me he encontrado con muchos artículos que tienen un enfoque similar al presentado aquí y que muestran gráficos de predicciones de precios de criptomonedas que se parecen a la anterior.

La precisión aparentemente sorprendente de las predicciones de precios debe activar las alarmas de inmediato.

Estos resultados son obviamente demasiado buenos para ser verdad.

"Cuando algo parece demasiado bueno para ser verdad, por lo general lo es". – Emmy Rossum

En lo que sigue, quiero demostrar por qué este es el caso.

No me malinterpreten: mi intención no es socavar el trabajo puesto en esos artículos. Son buenos y merecen los aplausos que recibieron. De hecho, muchos de esos enfoques son muy precisos, técnicamente hablando .

El objetivo de este artículo es poner de manifiesto por qué esos modelos son, en la práctica, falaces y por qué sus predicciones no son necesariamente adecuadas para su uso en el comercio real.

Entonces, ¿por qué exactamente es este el caso? Echemos un vistazo de cerca.

Predecir el precio de Bitcoin usando LSTM

Para explicarlo, permítame que lo guíe a través de un ejemplo de construcción de una red neuronal multidimensional de Long Short Term Memory (LSTM) para predecir el precio de Bitcoin que arroja los resultados de predicción que vio anteriormente.

Los LSTM son un tipo especial de redes neuronales recurrentes (RNN) , que son particularmente adecuadas para problemas de series de tiempo. Por lo tanto, se han vuelto populares al tratar de pronosticar los precios de las criptomonedas, así como los mercados bursátiles.

Para una introducción en profundidad a los LSTM, recomiendo este y este artículo.

Para la implementación actual de LSTM, utilicé Python y Keras . (Puede encontrar el cuaderno Jupyter correspondiente con el código completo en mi Github ).

1. Obtener los datos

Primero, busqué datos históricos sobre el precio de Bitcoin (también puede hacer esto para cualquier otra criptomoneda). Para hacerlo, utilicé la API de cryptocompare :

 importar json 
solicitudes de importación
importar pandas como pd
 endpoint = 'https://min-api.cryptocompare.com/data/histoday' 
res = requests.get (punto final + '? fsym = BTC & tsym = USD y límite = 2000')
hist = pd.DataFrame (json.loads (res.content) ['Data'])
hist = hist.set_index ('tiempo')
hist.index = pd.to_datetime (hist.index, unidad = 's')
hist.head ()

Una instantánea de los datos históricos de precios de Bitcoin.

Voilà, datos diarios históricos de BTC de los últimos 2000 días, desde 2012-10-10 hasta 2018-04-04 .

2. Split de prueba de tren

Luego, dividí los datos en un entrenamiento y un conjunto de prueba . Usé el último 10% de los datos para las pruebas, que divide los datos en el 14-09-2014. Todos los datos anteriores a esta fecha se usaron para el entrenamiento, todos los datos a partir de esta fecha se usaron para probar el modelo capacitado. A continuación, tracé la columna más close de nuestro DataFrame, que es el precio de cierre diario que pretendo predecir.

 def train_test_split (df, test_size = 0.1): 
split_row = len (df) - int (test_size * len (df))
train_data = df.iloc [: split_row]
test_data = df.iloc [split_row:]
return train_data, test_data
 def line_plot (line1, line2, label1 = None, label2 = None, title = ''): 
fig, ax = plt.subplots (1, figsize = (16, 9))
ax.plot (line1, label = label1, linewidth = 2)
ax.plot (line2, label = label2, linewidth = 2)
ax.set_ylabel ('price [USD]', fontsize = 14)
ax.set_title (title, fontsize = 18)
ax.legend (loc = 'best', fontsize = 18)
 train, test = train_test_split (hist, test_size = 0.1) 
line_plot (train.close, test.close, 'entrenamiento', 'prueba', 'BTC')

Prueba de tren dividida de datos históricos de precios de Bitcoin

3. Construyendo el Modelo

Para entrenar el LSTM, los datos se dividieron en ventanas de 7 días (este número es arbitrario, simplemente elegí una semana aquí) y dentro de cada ventana normalicé los datos a base cero , es decir, la primera entrada de cada ventana es 0 y todo otros valores representan el cambio con respecto al primer valor. Por lo tanto, estoy prediciendo cambios de precios, en lugar de precios absolutos.

 def normalise_zero_base (df): 
"" "Normalizar el marco de datos en columnas para reflejar los cambios con
respecto a la primera entrada.
"" "
return df / df.iloc [0] - 1
 def extract_window_data (df, window = 7, zero_base = True): 
"" "Convertir marco de datos a secuencias / ventanas superpuestas de
longitud `ventana`.
"" "
window_data = []
para idx en el rango (len (df) - window):
tmp = df [idx: (idx + ventana)] copia ()
si zero_base:
tmp = normalise_zero_base (tmp)
window_data.append (tmp.values)
return np.array (window_data)
 def prepare_data (df, window = 7, zero_base = True, test_size = 0.1): 
"" "Preparar datos para LSTM." ""
# prueba de tren dividida
train_data, test_data = train_test_split (df, test_size)

# extraer datos de ventana
X_train = extract_window_data (train_data, window, zero_base)
X_test = extract_window_data (test_data, window, zero_base)

# objetivos de extracto
y_train = train_data.close [window:]. values
y_test = test_data.close [window:]. values
si zero_base:
y_train = y_train / train_data.close [: - window] .values ??- 1
y_test = y_test / test_data.close [: - window] .values ??- 1
 return train_data, test_data, X_train, X_test, y_train, y_test 
 train, test, X_train, X_test, y_train, y_test = prepare_data (hist) 

Utilicé una red neural simple con una única capa LSTM que consta de 20 neuronas, un factor de abandono de 0.25 y una capa Densa con una función de activación lineal única. Además, usé Mean Absolute Error (MAE) como función de pérdida y el optimizador de Adam .

Entrené la red durante 50 épocas con un tamaño de lote de 4 .

Nota: La elección de la arquitectura de red y todos los parámetros es arbitraria y no los optimicé para ninguno, ya que este no es el objetivo de este artículo.

 def build_lstm_model (input_data, output_size, neurons = 20, 
activ_func = 'linear', dropout = 0.25,
loss = 'mae', optimizador = 'adam'):
modelo = Secuencial ()
 model.add (LSTM (neuronas, input_shape = ( 
input_data.shape [1], input_data.shape [2])))
model.add (Dropout (dropout))
model.add (Dense (units = output_size))
model.add (Activación (activ_func))
 model.compile (pérdida = pérdida, optimizador = optimizador) 
modelo de devolución
 model = build_lstm_model (X_train, output_size = 1) 
history = model.fit (X_train, y_train, epochs = 50, batch_size = 4)

4. Resultados

Usando el modelo entrenado para predecir en el conjunto de prueba de la izquierda, obtenemos el gráfico que se muestra al principio de este artículo.

Entonces, ¿qué es exactamente lo que está mal con estos resultados?

¿Por qué no deberíamos usar este modelo para el comercio real?

Echemos un vistazo más de cerca y hagamos zoom en los últimos 30 días de la trama.

 targets = test [target_col] [window:] 
preds = model.predict (X_test) .squeeze ()
# convertir predicciones de cambio de nuevo a precio real
preds = test.close.values ??[: - window] * (preds + 1)
preds = pd.Series (index = targets.index, data = preds)
 n = 30 
 line_plot (targets [-n:], preds [-n:], 'actual', 'predicción') 

¿Mira eso?

Es posible que ya haya adivinado correctamente que la falla fundamental con este modelo es que para la predicción de un día en particular, está usando principalmente el valor del día anterior.

La línea de predicción no parece ser mucho más que una versión modificada del precio real.

De hecho, si ajustamos las predicciones y las cambiamos por un día, esta observación se vuelve aún más obvia.

 line_plot (targets [-n:] [: - 1], preds [-n:]. shift (-1)) 

Como puede ver, de repente observamos una coincidencia casi perfecta entre los datos reales y las predicciones, lo que indica que el modelo esencialmente está aprendiendo el precio el día anterior.

Estos resultados son exactamente lo que he estado viendo en muchos de los ejemplos que usan predicciones de punto único con LSTM.

Para aclarar este punto, calculemos los rendimientos esperados según lo predicho por el modelo y los comparemos con los rendimientos reales.

 actual_returns = targets.pct_change () [1:] 
predicted_returns = preds.pct_change () [1:]

Al observar los rendimientos reales y previstos, tanto en su forma original como con el cambio de 1 día aplicado a ellos, obtenemos la misma observación.

Resultados reales y pronosticados. En el diagrama izquierdo, las predicciones se ajustan por día.

En realidad, si calculamos la correlación entre los rendimientos reales y los pronosticados tanto para las predicciones originales como para las ajustadas por día, podemos hacer la siguiente observación:

 fig, (ax1, ax2) = plt.subplots (1, 2, figsize = (18, 9)) 
 # correlación real 
corr = np.corrcoef (actual_returns, predicted_returns) [0] [1]
ax1.scatter (actual_returns, predicted_returns, color = 'k')
ax1.set_title ('r = {: .2f}'. format (corr), fontsize = 18)
 # correlación desplazada 
shifted_actual = actual_returns [: - 1]
shifted_predicted = predicted_returns.shift (-1) .dropna ()
corr = np.corrcoef (shifted_actual, shifted_predicted) [0] [1]
ax2.scatter (shifted_actual, shifted_predicted, color = 'k')
ax2.set_title ('r = {: .2f}'. formato (corr));

Como puede ver en los gráficos anteriores, los rendimientos reales y previstos no están correlacionados. Solo después de aplicar el turno de 1 día a las predicciones obtenemos retornos altamente correlacionados que se asemejan a los retornos de los datos reales de bitcoins.

Resumen

El objetivo de este blog fue abordar los muchos ejemplos de predicciones de criptomonedas y precios del mercado bursátil utilizando redes neuronales profundas que he encontrado en los últimos dos meses; estos adoptan un enfoque similar al empleado aquí: Implementar un LSTM usando datos históricos de precios para predecir los resultados futuros. He demostrado por qué estos modelos pueden no ser necesariamente viables para el comercio real.

Sí, la red puede efectivamente aprender. Pero termina usando una estrategia en la que la predicción de un valor cercano al anterior resulta exitosa en términos de minimizar el error absoluto medio.

Sin embargo, no importa qué tan precisas sean las predicciones en términos del error de pérdida, en la práctica, los resultados de los modelos de predicción de punto único basados ??en datos de precios históricos solo , como el que se muestra aquí, siguen siendo difíciles de lograr y no son particularmente útiles para comercio.

Huelga decir que potencialmente existen enfoques más sofisticados para implementar LSTM útiles para las predicciones de precios. El uso de más datos, así como la optimización de la arquitectura de red y los hiperparámetros son un comienzo. En mi opinión, sin embargo, hay más potencial para incorporar datos y funciones que van más allá de los precios históricos. Después de todo, el mundo de las finanzas ya sabe desde hace tiempo que "el rendimiento pasado no es un indicador de los resultados futuros ".

Y lo mismo podría aplicarse a las criptomonedas.

Descargo de responsabilidad: Esto no es un consejo financiero. El artículo y el modelo presentado son solo para fines educativos. No lo use para negociar o tomar decisiones de inversión.