微信号:ZXL_LHTZ_JQXX

介绍:介绍关于量化投资和机器学习方面的知识.通过文摘,报告,论坛,博文,代码,还有博主自己研究的领域,给大家提供无偿的知识干粮.

【干货】教你如何鉴别那些用深度学习预测股价的花哨模型?

2019-01-13 16:39 量化投资与机器学习

标星★公众号第一时间获取最新资讯


本期作者:Rafael

本期翻译:Chloe | 公众号翻译部成员


年度巨献↓↓

↑↑点我


近期原创文章:

 利用深度学习最新前沿预测股价走势

 一位数据科学PhD眼中的算法交易

 深度学习是信号处理和时序分析的最后选择?

 人工智能『AI』应用算法交易,7个必踩的坑!

 神经网络在算法交易上的应用系列(一)

 预测股市 | 如何避免p-Hacking,为什么你要看涨?


正文


我们建立了一个深度神经网络模型来预测比特币价格——测试结果出奇地准确!


看看这个结果吧:



看起来相当精确,不是吗?


在你问我之前我先回答你:是的,上面的回测只用以前的数据去训练模型(稍后会给出细节)。


所以这是一个可以让我变富有的印钞机!


好了,让我们就此打住吧!不要这样做。


我再说一遍:不要这样做!不要用它来交易!


请不要被欺骗了。


上面的结果是极其具有迷惑性的,让我来解释一下。


太美好以至于不够真实


在过去几周甚至几个月,我们碰到了好多类似文章,用类似的方法给读者呈现了关于加密货币价格预测的类似上图的结果。


当你看到这些看起来非常精确的结果时,就应该给自己敲响警钟。


这些结果明显太美好而感觉不够真实。


“When something looks too good to be true, it usually is.” — Emmy Rossum


接下来,我们来一步步证明为什么是这样的。


请不要理解错了——我的意图并不是低估那些文章的价值。他们很好,应当获得掌声。事实上,从技术层面讲许多这样的方法都是非常精确的。


本文的目的在于解释为什么那些模型在实际应用中靠不住,为什么他们的预测结果不一定适合于实际交易。


那为什么是这样的呢?让我们一起看个究竟。


用LSTMs来预测比特币价格


为了更好地解释,我先给你一个实例。该实例通过建立多维Long Short Term Memory (LSTM) 神经网络模型来预测比特币价格,并产生了如上图你所看到的一样精确的预测结果。


LSTMs 是一类特殊的Recurrent Neural Networks (RNN))模型,特别适合时间序列问题。因此,LSTM在预测加密货币或者股票价格的模型中非常流行。


关于深度介绍LSTMs的文章,推荐这两篇:


1、http://colah.github.io/posts/2015-08-Understanding-LSTMs/


2、http://blog.echen.me/2017/05/30/exploring-lstms/


目前我们用Python和Keras来实现LSTM算法。


1. 获得数据

首先,我通过API从cryptocompare获得比特币的历史价格数据(对于其他任何加密货币或者股票你都可以这么做)。

 

import json
import requests

from keras.models import Sequential
from keras.layers import Activation, Dense, Dropout, LSTM
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.metrics import mean_absolute_error

sns.set_palette('Set2')
%matplotlib inline

endpoint = 'https://min-api.cryptocompare.com/data/histoday'
res = requests.get(endpoint + '?fsym=BTC&tsym=USD&limit=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()

我们获得了从2012年10月10日到2018年04月04日共约2000天的BTC每日数据。


2. 训练集和测试集分组

然后,我将所有数据分成训练集和测试集两部分。用最近10%的数据作为测试集,也就是说将数据从2017年09月14日那天分开。在那天之前的所有数据作为训练集,那天以及那天之后的所有数据用于测试这个模型。接下来画出了我们的DataFrame数据结构中close那一列的数据,也就是我想预测的每天的收盘价格。


target_col = 'close'

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='', lw=2):
   fig, ax = plt.subplots(1, figsize=(169))
   ax.plot(line1, label=label1, linewidth=lw)
   ax.plot(line2, label=label2, linewidth=lw)
   ax.set_ylabel('price [USD]', fontsize=14)
   ax.set_title(title, fontsize=18)
   ax.legend(loc='best', fontsize=18);

line_plot(train[target_col], test[target_col], 'training''test', title='BTC')

比特币历史数据的训练集和测试集分离


3. 建立模型

为了训练LSTM模型,将所有训练数据连续划分成互不相交的窗口,每个窗口有7天(这个数字可以任意选,我只是简单地选了7天)。在每个窗口内,我将数据正规化成零基准,也就是说,每个窗口第一个元素是0而其他值代表着相应于第一个元素的变化率。因此,我在预测价格的变化率,而不是绝对的数值大小。

 

def normalise_zero_base(df):
   """ Normalise dataframe column-wise to reflect changes with respect to first entry. """
   return df / df.iloc[0] - 1

def normalise_min_max(df):
   """ Normalise dataframe column-wise min/max. """
   return (df - df.min()) / (data.max() - df.min())

def extract_window_data(df, window_len=10, zero_base=True):
   """ Convert dataframe to overlapping sequences/windows of len `window_data`.
   
       :param window_len: Size of window
       :param zero_base: If True, the data in each window is normalised to reflect changes
           with respect to the first entry in the window (which is then always 0)
   """

   window_data = []
   for idx in range(len(df) - window_len):
       tmp = df[idx: (idx + window_len)].copy()
       if zero_base:
           tmp = normalise_zero_base(tmp)
       window_data.append(tmp.values)
   return np.array(window_data)

def prepare_data(df, target_col, window_len=10, zero_base=True, test_size=0.2):
   """ Prepare data for LSTM. """
   # train test split
   train_data, test_data = train_test_split(df, test_size=test_size)
   
   # extract window data
   X_train = extract_window_data(train_data, window_len, zero_base)
   X_test = extract_window_data(test_data, window_len, zero_base)
   
   # extract targets
   y_train = train_data[target_col][window_len:].values
   y_test = test_data[target_col][window_len:].values
   if zero_base:
       y_train = y_train / train_data[target_col][:-window_len].values - 1
       y_test = y_test / test_data[target_col][:-window_len].values - 1

   return train_data, test_data, X_train, X_test, y_train, y_test


只用一个简单的神经网络模型。这个神经网络带一个LSTM层(包含20个神经元和一个失效因子dropout factor 0.25)和一个包含单个线性激发函数的稠密层。另外,我用均值绝对误差(Mean Absolute Error)作为损失函数和Adam优化器。


我训练这个模型50次(epochs),每次批量大小(batch size)为4。


说明:网络构架和所有因子的选择都是任意的,我并没有对他们做优化,因为这不是本文的着重点。

 

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(Dropout(dropout))
   model.add(Dense(units=output_size))
   model.add(Activation(activ_func))

   model.compile(loss=loss, optimizer=optimizer)
   return model

np.random.seed(42)

# data params
window_len = 7
test_size = 0.1
zero_base = True

# model params
lstm_neurons = 20
epochs = 50
batch_size = 4
loss = 'mae'
dropout = 0.25
optimizer = 'adam'

train, test, X_train, X_test, y_train, y_test = prepare_data(
   hist, target_col, window_len=window_len, zero_base=zero_base, test_size=test_size)

model = build_lstm_model(
   X_train, output_size=1, neurons=lstm_neurons, dropout=dropout, loss=loss,
   optimizer=optimizer)
history = model.fit(
   X_train, y_train, epochs=epochs, batch_size=batch_size, verbose=1, shuffle=True)
Epoch 1/50


4. 结果

用训练好的模型去预测剩余的测试集,我们得到了本文一开始的图。

targets = test[target_col][window_len:]
preds = model.predict(X_test).squeeze()

mean_absolute_error(preds, y_test)
0.044705889968596577

preds = test[target_col].values[:-window_len] * (preds + 1)
preds = pd.Series(index=targets.index, data=preds)

line_plot(targets, preds, 'actual', 'prediction', lw=3)


那这个结果到底是哪里出问题了呢?


为什么我们不能将这个模型用于实际交易?


我们将这个图放大到最近的30天,然后仔细观察一下。

 

targets = test[target_col][window:]
preds = model.predict(X_test).squeeze()
# convert change predictions back to actual price
preds = test.close.values[:-window] * (preds + 1)
preds = pd.Series(index=targets.index, data=preds)
n = 30
line_plot(targets[-n:], preds[-n:], 'actual', 'prediction')


看到了吗?估计你已经准确地猜到了,这个模型的基本错误是当做某一天的预测时,基本只用到了前一天的值。


那条红色的预测曲线,看起来基本只是那条绿色的实际价格曲线的平移而已。


事实上,如果我们将预测曲线调整一下,往前平移一天,那我们所观察到的现象会更显而易见。

 

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


正如你所看到的,我们几乎可以观察到实际数据和预测数据的一个近乎完美的重合。也就是说,我们的模型本质上只学习了前一天的价格。


这样的结果正是我在许多用LSTM做单点预测的事例中看到的。


为了揭示得更清晰,让我们来计算预测价格回报的期望,然后跟实际回报的期望做对比。


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

def dual_line_plot(line1, line2, line3, line4, label1=None, label2=None, title='', lw=2):
   import matplotlib.dates as mdates
   fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(21, 9))
   ax1.plot(line1, label=label1, linewidth=lw)
   ax1.plot(line2, label=label2, linewidth=lw)
   ax2.plot(line3, label=label1, linewidth=lw)
   ax2.plot(line4, label=label2, linewidth=lw)
   ax2.set_xticks(ax1.get_xticks())
   ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
   ax1.set_ylabel('daily returns', fontsize=14)
   ax2.legend(loc='best', fontsize=18);

dual_line_plot(actual_returns[-n_points:],
         predicted_returns[-n_points:],
         actual_returns[-n_points:][:-1],
         predicted_returns[-n_points:].shift(-1),
         'actual returns', 'predicted returns', lw=3)

 实际回报和预测回报,右图中预测回报往前平移了一天


line_plot(actual_returns[-n_points:][:-1], predicted_returns[-n_points:].shift(-1),
          'actual returns', 'predicted returns', lw=3)


不论是原始的形式还是平移一天的形式,如果我们看实际回报和预测的回报,我们可以得到相同的观察结论。


事实上,如果我们计算实际回报和预测回报之前的相关性,不论是原始的预测还是平移一天的预测,我们都可以得到如下观察结果:


fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 9))

# actual correlation
corr = np.corrcoef(actual_returns, predicted_returns)[0][1]
ax1.scatter(actual_returns, predicted_returns, color='k', marker='o', alpha=0.5, s=100)
ax1.set_title('r = {:.2f}'.format(corr), fontsize=18)

# shifted correlation
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', marker='o', alpha=0.5, s=100)
ax2.set_title('r = {:.2f}'.format(corr), fontsize=18);


从上图我们可以看到,比特币价格的实际回报和原始预测回报之间没有相关性,而和平移一天后的预测回报之间有非常高的相关性。


总结


本文目的在于总结在过去几个月遇到的用深度神经网络来预测加密货币或者股票价格的例子。这些例子与本文用了完全类似的方法:都是用历史数据通过LSTM来预测未来结果我已经证明了为什么这样的模型在实际交易中可能不可靠。


没错,深度神经网络可以很有效地学习。但它最终训练得到的策略竟是预测一个跟前一天数据非常接近的数值,以成功地实现最小化均值绝对误差。


然而,不管这个预测从均值绝对误差的角度看有多精确,实际上,正如我们文中例子展现的那样,仅仅基于历史数据的单点预测模型的结果依然很难有所作为,尤其在实际交易中。


毋庸置疑,更复杂且实施有效的用于价格预测的LSTM模型可能是存在的。首先可以使用更多的数据,优化网络架构和参数。不过在我看来,引入区别于历史数据的其他数据和特征会更有用,毕竟金融股市的久存名言说:


“过去不能代表未来”


文章来源:https://hackernoon.com/dont-be-fooled-deceptive-cryptocurrency-price-predictions-using-deep-learning-bf27e4837151


欢迎大家在文末给公众号翻译部的小伙伴们打赏!


推荐阅读


01、经过多年交易之后你应该学到的东西(深度分享)

02、监督学习标签在股市中的应用(代码+书籍)

03、全球投行顶尖机器学习团队全面分析

04、使用Tensorflow预测股票市场变动

05、使用LSTM预测股票市场基于Tensorflow

06、美丽的回测——教你定量计算过拟合概率

07、利用动态深度学习预测金融时间序列基于Python

08、Facebook开源神器Prophet预测时间序列基于Python

09、Facebook开源神器Prophet预测股市行情基于Python

10、2018第三季度最受欢迎的券商金工研报前50(附下载)

11、实战交易策略的精髓(公众号深度呈现)

12、Markowitz有效边界和投资组合优化基于Python

13、使用LSTM模型预测股价基于Keras

14、量化金融导论1:资产收益的程式化介绍基于Python

15、预测股市崩盘基于统计机器学习与神经网络(Python+文档)

16、实现最优投资组合有效前沿基于Python(附代码)

17、精心为大家整理了一些超级棒的机器学习资料(附链接)

18、海量Wind数据,与全网用户零距离邂逅!

19、机器学习、深度学习、量化金融、Python等最新书籍汇总下载

20、各大卖方2019年A股策略报告,都是有故事的人!


扫码关注我们

 
量化投资与机器学习 更多文章 一位数据科学PhD眼中的算法交易 【2万字干货】利用深度学习最新前沿预测股价走势 不用接私活!普通程序员学会这个也能年入百万! 清华毕业就要月薪3万!你凭什么? 【独家】人工智能『AI』应用算法交易,7个必踩的坑!
猜您喜欢 Ceph性能优化 之 配置参数优化 微软Build 2015大会上海分站 程序员,你敢不敢像科比一样努力! 昨晚苹果ARKit斗鱼吹水会录像 工具 | 常用工具镜像网站又更新了(Golang、Chrome、VsCode、NDK等) #3