神经网络在算法交易上的应用系列——简单时序预测
我们想从零实现只基于深度学习模型的交易系统,对于在研究过程中我们遇到的任何问题(价格预测,交易策略,风险管理)我们都将采用不同类型的人工神经网络(ANNS)来解决,同时也会检验它们在处理这些问题的效果到底如何。
在第一部分,我们想演示MLPs(多层感知机),CNNs(卷积神经网络)和RNN(递归或循环神经网络)是如何应用到时间序列预测上的。这部分中,我们不准备使用任何特征工程。而只考虑S&P500指数价格变化的历史数据。我们有1950年到2016年每天的开、高、低、收和成交量数据。首先,我们尝试预测下一个交易日最后的收盘价,然后,尝试预测收益率(收到开)。从Yahoo Finance下载数据。
链接:
https://finance.yahoo.com/quote/%5EGSPC/history?ltr=1
问题定义
我们把我们问题看作:
1、回归问题(预测具体的收盘价格或第二天的收益率)
2、二分类问题(价格上涨[1;0]或下跌[0;1])
我们将使用Keras框架对NNs进行训练。
首先让我们准备训练数据。我们想根据N天前的信息来预测t+1的值。例如,有过去30天的行情数据,我们想预测明天,即第31天的价格是多少。
我们使用了90%的时间序列作为训练集(把它当作历史数据),剩下的10%作为模型评估的测试集。
这里是导入数据,将数据分成训练集和原始数据的预处理例子:
def load_snp_close(): f = open('table.csv', 'rb').readlines()[1:] raw_data = [] raw_dates = [] for line in f: try: close_price = float(line.split(',')[4]) raw_data.append(close_price) raw_dates.append(line.split(',')[0]) except: continue return raw_data, raw_dates def split_into_chunks(data, train, predict, step, binary=True, scale=True): X, Y = [], [] for i in range(0, len(data), step): try: x_i = data[i:i+train] y_i = data[i+train+predict] if binary: if y_i > 0.: y_i = [1., 0.] else: y_i = [0., 1.] if scale: x_i = preprocessing.scale(x_i) else: timeseries = np.array(data[i:i+train+predict]) if scale: timeseries = preprocessing.scale(timeseries) x_i = timeseries[:-1] y_i = timeseries[-1] except: break X.append(x_i) Y.append(y_i) return X, Y
回归问题 MLP
它只是含2个隐藏层的感知器。隐藏神经元的数量是根据经验选择的,我们将在下一个部分进行超参数的最优化。在两个隐藏层之间,我们添加一个退出层来防止过拟合。
编译器中重要的是Dense(1),Activation(‘Linear’) 和 ‘mse’。我们想得到一个可以在任意范围内的输出结果(我们预测真实价格),损失函数定义成均方差。
model = Sequential() model.add(Dense(500, input_shape = (TRAIN_SIZE, ))) model.add(Activation('relu')) model.add(Dropout(0.25)) model.add(Dense(250)) model.add(Activation('relu')) model.add(Dense(1)) model.add(Activation('linear')) model.compile(optimizer='adam', loss='mse')
让我们看看如果我们仅输入20天的收盘价来预测第21天的价格结果会是什么样的。最终MSE= 46.3635263557,但它不是非常具有代表性的信息。下面是测试集前150个点的预测图,黑线是实际数据,蓝线是预测数据。我们可以清楚地看到,我们的算法甚至都不接近真实值,但可以学习到趋势。
predicted = model.predict(X_test) try: fig = plt.figure(figsize=(width, height)) plt.plot(Y_test[:150], color='black') plt.plot(predicted[:150], color='blue') plt.show() except Exception as e: print str(e)
让我们使用sklearn的方法 preprocessing.scale() 把时间序列数据标准化为均值为0,方差为1的序列。然后用同样的MLP来训练。现在我们有了MSE = 0.0040424330518(但是它是基于标准化的数据)。在下面的图中,你可以看到标准化的时间序列(黑色)和我们的预测值(蓝色):
实际中我们使用这个模型时,我们需要对时间序列进行去标准化。我们可以通过乘上用来预测的时间序列的20天标准差,然后加上它的均值来实现:
params = [] for xt in X_testp: xt = np.array(xt) mean_ = xt.mean() scale_ = xt.std() params.append([mean_, scale_]) predicted = model.predict(X_test) new_predicted = [] for pred, par in zip(predicted, params): a = pred*par[1] a += par[0] new_predicted.append(a)
这个例子中MSE等于937.963649937. 下图是还原的预测值(红色)和真实数据(绿色):
是不是还可以? 但是,让我们尝试更加复杂的算法来解决这个问题。
回归问题 CNN
我们不打算深入讲解卷积神经网络理论,但你可以查看下列非常棒的资源:
1、Stanford CNNs for Computer Vision course
链接:http://cs231n.github.io
2、Denny Britz的博客,内容真的超级棒!
链接:http://www.wildml.com
让我们定义一个带一个全连接层的两层卷积神经网络(卷积层和池化层组合),输出和前面的相同:
model = Sequential() model.add(Convolution1D(input_shape = (TRAIN_SIZE, EMB_SIZE), nb_filter=64, filter_length=2, border_mode='valid', activation='relu', subsample_length=1)) model.add(MaxPooling1D(pool_length=2)) model.add(Convolution1D(input_shape = (TRAIN_SIZE, EMB_SIZE), nb_filter=64, filter_length=2, border_mode='valid', activation='relu', subsample_length=1)) model.add(MaxPooling1D(pool_length=2)) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(250)) model.add(Dropout(0.25)) model.add(Activation('relu')) model.add(Dense(1)) model.add(Activation('linear'))
检验我们的结果。标准化的和恢复的数据MSE分别是0.227074542433和935.520550172.如图所示:
即便是看标准化数据的MSE,这个神经网络学习效果更差。最有可能的是,更深层的架构需要更多的数据来训练,否则只是大量过滤或层数造成的过拟合。
回归问题 RNN
作为循环架构,我想去用两个堆叠的LSTM层。
更多关于LSTM信息读这里:
链接:
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
model = Sequential() model.add(LSTM(input_shape = (EMB_SIZE,), input_dim=EMB_SIZE, output_dim=HIDDEN_RNN, return_sequences=True)) model.add(LSTM(input_shape = (EMB_SIZE,), input_dim=EMB_SIZE, output_dim=HIDDEN_RNN, return_sequences=False)) model.add(Dense(1)) model.add(Activation('linear'))
预测图如下:
MSEs=0.0246238639582,939.948636707
RNN预测看起来更像移动平均模型,他不能学习和预测所有的波动。
所以,这是一个有点出乎意料的结果,但我们可以看到,对于这个时间序列的预测,MLPs工作得更好。让我们看看如果我们从回归转变到分类问题的结果。现在我们不用收盘价,而用每天收益(收盘到开盘),我们想基于过去20天的日收益率来预测收盘价比开盘价高还是低。
分类问题 MLP
代码只做了微调,我们改变了最后的Dense 层,令结果为[0;1]或[1;0],并增加逻辑回归使输出结果为期望概率。
为了得到二元输出,下面代码做修改:
split_into_chunks(timeseries, TRAIN_SIZE, TARGET_TIME, LAG_SIZE, binary=False, scale=True) split_into_chunks(timeseries, TRAIN_SIZE, TARGET_TIME, LAG_SIZE, binary=True, scale=True)
同样,我们修改损失函数为二元交叉熵,并增加准确率指标。
model = Sequential() model.add(Dense(500, input_shape = (TRAIN_SIZE, ))) model.add(Activation('relu')) model.add(Dropout(0.25)) model.add(Dense(250)) model.add(Activation('relu')) model.add(Dense(2)) model.add(Activation('softmax')) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
不比随机猜测好到哪去(50%准确率),我们尝试些更好的。看看下面的结果。
分类问题CNN
model = Sequential() model.add(Convolution1D(input_shape = (TRAIN_SIZE, EMB_SIZE), nb_filter=64, filter_length=2, border_mode='valid', activation='relu', subsample_length=1)) model.add(MaxPooling1D(pool_length=2)) model.add(Convolution1D(input_shape = (TRAIN_SIZE, EMB_SIZE), nb_filter=64, filter_length=2, border_mode='valid', activation='relu', subsample_length=1)) model.add(MaxPooling1D(pool_length=2)) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(250)) model.add(Dropout(0.25)) model.add(Activation('relu')) model.add(Dense(2)) model.add(Activation('softmax')) history = TrainingHistory() model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
分类问题CNN
model = Sequential() model.add(LSTM(input_shape = (EMB_SIZE,), input_dim=EMB_SIZE, output_dim=HIDDEN_RNN, return_sequences=True)) model.add(LSTM(input_shape = (EMB_SIZE,), input_dim=EMB_SIZE, output_dim=HIDDEN_RNN, return_sequences=False)) model.add(Dense(2)) model.add(Activation('softmax')) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
结论
我们可以看到,将时间序列预测作为回归问题对待的方法更好,它可以学习到序列的趋势并且预测价格和真实值接近。
令我们吃惊的是,MLPs处理序列数据的效果比被认为更擅长处理时间序列数据的CNNs和RNNs更好。我是用非常小的数据集(16K时间序列)和虚拟超参数选择来解释的。
你可以使用文中代码来重现结果和获得更好的结果。
我们认为可以在回归和分类上得到更好的结果,通过使用不同的特征(不仅仅是标准化的时间序列),像一些技术指标等。我们还可以尝试更高频率的数据,比如说分钟数据,可以获得更多的训练数据。
原文链接:
https://medium.com/@alexrachnog/neural-networks-for-algorithmic-trading-part-one-simple-time-series-forecasting-f992daa1045a
原文发布于微信公众号 - 量化投资与机器学习(Lhtz_Jqxx)