Não se deixe enganar – Previsões de Preço de Criptomoedas Enganosas Usando Aprendizado Profundo

Por que você deve ser cauteloso com redes neurais para negociação

Então eu construí uma Deep Neural Network para prever o preço do Bitcoin – e é incrivelmente preciso.

Curioso?

Veja os resultados da previsão para você.

Parece bem preciso, não é?

E antes que você pergunte: Sim, a avaliação acima foi realizada em dados de teste não vistos – apenas dados anteriores foram usados ??para treinar o modelo (mais detalhes depois).

Portanto, esta é uma máquina de fazer dinheiro que posso usar para ficar rico!

Certo?

Na verdade, estou dando o código para o modelo acima, para que você possa usá-lo você mesmo …

Ok, pare aí mesmo . Não faça isso.

Eu repito: não faça isso! Não use para negociação.

Não se deixe enganar.

Há algo totalmente enganoso sobre esses resultados.

Deixe-me explicar.

Muito bom para ser verdade

Durante as duas últimas semanas e meses, encontrei muitos artigos que adotam uma abordagem semelhante à apresentada aqui e que mostram gráficos de predições de preço de criptomoedas que se parecem com o acima.

A precisão aparentemente estonteante das previsões de preços deve desencadear imediatamente os alarmes.

Esses resultados são obviamente bons demais para serem verdade.

“Quando algo parece bom demais para ser verdade, geralmente é.” – Emmy Rossum

A seguir, quero demonstrar por que esse é o caso.

Não me entenda mal – minha intenção não é minar o trabalho colocado nesses artigos. Eles são bons e merecem as palmas que receberam. De fato, muitas dessas abordagens são muito precisas – tecnicamente falando .

O objetivo deste artigo é mostrar por que esses modelos são, na prática, falaciosos e por que suas previsões não são necessariamente adequadas para uso em negociações reais.

Então, por que exatamente é esse o caso? Vamos dar uma olhada de perto.

Previsão do preço do Bitcoin usando LSTMs

Para explicar, deixe-me guiá- lo através de um exemplo de construção de uma rede neural multidimensional de Long Short Term Memory (LSTM) para prever o preço do Bitcoin que produz os resultados de previsão que você viu acima.

Os LSTMs são um tipo especial de Redes Neurais Recorrentes (RNN) , que são particularmente adequadas para problemas de séries temporais. Assim, eles se tornaram populares quando tentam prever os preços da criptomoeda, bem como os mercados de ações.

Para introduções detalhadas aos LSTMs eu recomendo este e este artigo.

Para a implementação atual do LSTM, usei Python e Keras . (Você pode encontrar o Notebook Jupyter correspondente com o código completo no meu Github .)

1. Obtendo os dados

Primeiro, obtive dados históricos de preços do Bitcoin (você também pode fazer isso para qualquer outra criptomoeda). Para fazer isso, usei a API do cryptocompare :

 importar json 
 pedidos de importação 
 importar pandas como pd
 endpoint = 'https://min-api.cryptocompare.com/data/histoday' 
 res = requests.get (ponto de extremidade + '? fsym = BTC e tsym = USD & limite = 2000') 
 hist = pd.DataFrame (json.loads (res.content) ['Data']) 
 hist = hist.set_index ('time') 
 hist.index = pd.to_datetime (hist.index, unit = 's') 
 hist.head ()

Um instantâneo dos dados históricos de preços do Bitcoin.

Voilà, dados históricos diários do BTC nos últimos 2000 dias, de 2012-10-10 até 2018-04-04 .

2. Split de teste de trem

Em seguida, divido os dados em um treinamento e um conjunto de testes . Eu usei os últimos 10% dos dados para testes, que dividem os dados em 2017–09–14. Todos os dados antes desta data foram usados ??para treinamento, todos os dados a partir desta data foram usados ??para testar o modelo treinado. Abaixo, eu plotei a coluna de close de nosso DataFrame, que é o preço de fechamento diário que eu pretendia prever.

 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 = Nenhum, label2 = Nenhum, 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, 'training', 'teste', 'BTC')

Divisão de teste de trem dos dados históricos de preço do Bitcoin

3. Construindo o Modelo

Para treinar o LSTM, os dados foram divididos em janelas de 7 dias (esse número é arbitrário, eu simplesmente escolhi uma semana aqui) e dentro de cada janela eu normalizei os dados para zero base , ou seja, a primeira entrada de cada janela é 0 e todos outros valores representam a mudança em relação ao primeiro valor. Por isso, estou prevendo mudanças de preço, em vez de preço absoluto.

 def normalise_zero_base (df): 
 "" "Normalize o dataframe na coluna para refletir as alterações com 
 em relação à primeira entrada. 
 "" " 
 return df / df.iloc [0] - 1
 def extract_window_data (df, janela = 7, zero_base = Verdadeiro): 
 "" "Converter dataframe em seqüências / janelas sobrepostas de 
 comprimento `janela`. 
 "" " 
 window_data = [] 
 para idx no intervalo (len (df) - window): 
 tmp = df [idx: (idx + janela)]. copy () 
 se zero_base: 
 tmp = normalise_zero_base (tmp) 
 window_data.append (tmp.values) 
 return np.array (window_data)
 def prepare_data (df, janela = 7, zero_base = True, test_size = 0.1): 
 "" "Preparar dados para o LSTM." "" 
 # split de teste de trem 
 train_data, test_data = train_test_split (df, test_size) 

 # extrair dados da janela 
 X_train = extract_window_data (train_data, window, zero_base) 
 X_test = extract_window_data (test_data, window, zero_base) 

 # extrair alvos 
 y_train = train_data.close [window:]. valores 
 y_test = test_data.close [window:]. 
 se 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
 treinar, testar, X_train, X_test, y_train, y_test = prepare_data (hist)

Eu usei uma rede neural simples com uma única camada de LSTM consistindo de 20 neurônios, um fator de desistência de 0.25 e uma camada Densa com uma única função de ativação linear . Além disso, usei o Mean Absolute Error (MAE) como função de perda e o otimizador Adam .

Eu treinei a rede por 50 épocas com um tamanho de lote de 4 .

Nota: A escolha da arquitetura de rede e todos os parâmetros é arbitrária e eu não otimizei para nenhum deles, já que este não é o foco deste artigo.

 def build_lstm_model (input_data, output_size, neurons = 20, 
 activ_func = 'linear', dropout = 0,25, 
 loss = 'mae', optimizer = 'adam'): 
 model = Sequential ()
 model.add (LSTM (neurons, input_shape = ( 
 input_data.shape [1], input_data.shape [2]))) 
 model.add (eliminação (dropout)) 
 model.add (Dense (units = output_size)) 
 model.add (Ativação (activ_func))
 model.compile (loss = loss, optimizer = optimizer) 
 modelo de retorno
 model = build_lstm_model (X_train, output_size = 1) 
 history = model.fit (X_train, y_train, epochs = 50, batch_size = 4)

4. Resultados

Usando o modelo treinado para prever o conjunto de testes deixado de fora, obtemos o gráfico mostrado no começo deste artigo.

Então, o que exatamente está errado com esses resultados?

Por que não devemos usar esse modelo para negociação real?

Vamos dar uma olhada mais de perto e ampliar os últimos 30 dias da trama.

 alvos = teste [target_col] [window:] 
 preds = model.predict (X_test) .squeeze () 
 # converter previsões de alterações de volta ao preço real 
 preds = test.close.values ??[: - janela] * (preds + 1) 
 preds = pd.Series (index = targets.index, data = preds)
 n = 30
 line_plot (targets [-n:], preds [-n:], 'real', 'predição')

Veja isso?

Você já deve ter adivinhado corretamente que a falha fundamental com esse modelo é que, para a previsão de um determinado dia, ele está usando principalmente o valor do dia anterior.

A linha de previsão não parece ser muito mais do que uma versão deslocada do preço real.

De fato, se ajustarmos as previsões e mudá-las por um dia, essa observação se tornará ainda mais óbvia.

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

Como você pode ver, de repente observamos uma correspondência quase perfeita entre dados reais e previsões, indicando que o modelo está essencialmente aprendendo o preço no dia anterior.

Estes resultados são exatamente o que eu tenho visto em muitos dos exemplos usando predições de ponto único com LSTMs.

Para tornar este ponto mais claro, vamos calcular os retornos esperados conforme previsto pelo modelo e compará-los com os retornos reais.

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

Olhando para os retornos reais e previstos, tanto em sua forma original quanto com o turno de 1 dia aplicado a eles, obtemos a mesma observação.

Retornos reais e previstos. No gráfico da esquerda, as previsões são ajustadas por um dia.

Na verdade, se calcularmos a correlação entre retornos reais e previstos, tanto para as previsões originais como para aquelas ajustadas por um dia, podemos fazer a seguinte observação:

 fig, (ax1, ax2) = plt.subplots (1, 2, figsize = (18, 9))
 # correlação real 
 corr = np.corrcoef (real_returns, predicted_returns) [0] [1] 
 ax1.scatter (real_returns, predicted_returns, color = 'k') 
 ax1.set_title ('r = {: .2f}'. format (corr), fontsize = 18)
 Correlação deslocada 
 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}'. format (corr));

Como você pode ver nos gráficos acima, os retornos reais e previstos não são correlacionados. Somente depois de aplicar o turno de 1 dia nas previsões obtemos retornos altamente correlacionados que se assemelham aos retornos dos dados reais do bitcoin.

Resumo

O objetivo da postagem deste blog foi abordar os muitos exemplos de previsões de criptomoeda e preços de mercado de ações usando redes neurais profundas que eu encontrei nos últimos dois meses – eles adotam uma abordagem semelhante à empregada aqui: Implementando um LSTM usando dados históricos de preços para prever os resultados futuros. Eu demonstrei porque esses modelos podem não ser necessariamente viáveis ??para negociações reais.

Sim, a rede é efetivamente capaz de aprender. Mas acaba usando uma estratégia na qual prever um valor próximo ao anterior acaba sendo bem-sucedido em termos de minimizar o erro absoluto médio.

No entanto, não importa quão precisas sejam as previsões em termos de erro de perda – na prática, os resultados de modelos de previsão de ponto único baseados apenas em dados de preços históricos , como o mostrado aqui, continuam difíceis de realizar e não são particularmente úteis negociação.

Escusado será dizer que existem abordagens mais sofisticadas de implementar LSTMs úteis para previsões de preços. Usar mais dados, bem como otimizar a arquitetura de rede e os hiperparâmetros, é um começo. Na minha opinião, no entanto, há mais potencial em incorporar dados e recursos que vão além dos preços históricos. Afinal, o mundo das finanças já sabe há muito que “o desempenho passado não é um indicador para resultados futuros ”.

E o mesmo pode valer para criptomoedas.

Aviso: Este não é um conselho financeiro. O artigo e o modelo apresentado são apenas para fins educacionais. Não o use para negociação ou tomada de decisões de investimento.