前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >神经网络在算法交易上的应用系列——时序预测+回测

神经网络在算法交易上的应用系列——时序预测+回测

作者头像
量化投资与机器学习微信公众号
发布2019-02-26 16:33:12
1.6K0
发布2019-02-26 16:33:12
举报

本期作者:Alexandr Honchar

本期翻译:LIN | 公众号翻译部 这是公众号关于神经网络在金融领域特别是算法交易上的一个连载系列:

1、简单时间序列预测(已发表) 2、正确的时间序列预测+回测 3、多变量时间序列预测 4、波动率预测和自定义损失函数 5、多任务和多模式学习 6、超参数优化 7、用神经网络增强传统策略 8、概率编程和Pyro进行预测

欢迎大家关注公众号查看此系列。本期我们从讲第二部分。

前言

在第二部分中,我们想描述更正确处理金融数据的方法。与前一篇文章相比,我们想展示不同的数据标准化方法,并多讨论些过拟合的问题(在处理具有随机特性的数据时肯定会出现过拟合问题)。我们不会比较不同的架构(CNN, LSTM),你可以在之前的文章中查看它们。但即使只使用简单的前馈神经网络,我们也能看到一些重要的东西。

数据准备

让我们看看从2005年到今天苹果股价的历史时间序列。你可以很容易地从雅虎财经下载到csv格式的文件。在这个文件中数据的顺序是“颠倒”的——从2017年到2005年,所以我们需要先把它颠倒过来,如图:

代码语言:javascript
复制
data = pd.read_csv('./data/AAPL.csv')[::-1] 
close_price = data.ix[:, 'Adj Close'].tolist() 
plt.plot(close_price) 
plt.show()

正如我们在前一篇文章中所讨论的,我们可以用两种不同的方式来处理金融时间序列预测的问题(暂时忽略波动率预测、异常检测等有趣的话题):

我们将问题看作:

1、回归问题(试图准确预测收盘价或第二天的收益) 2、二元分类问题(价格将上升[1;0]或向下[0;1])

首先,我们准备训练数据。我们想根据N天前的信息来预测t+1的值。例如,有过去30天的收盘价数据,我们想预测明天,也就是第31天的价格是多少。我们将前90%的时间序列作为训练集(将其视为历史数据),后10%作为测试集来评估模型。

金融时间序列的主要问题是它们并不一定平稳,这意味着它们的统计特性(平均值、方差、最大值和最小值)随着时间的变化而变化,我们可以用ADF检验。正因为这种不平稳性,我们不能使用经典的数据标准化方法,如MinMax或Z-score标准化

在我们的例子中,我们将在分类问题上做一点“弊”。我们不需要预测某个确切的值,所以未来的期望值和方差对我们来说不是很有意义,我们只需要预测价格往上还是往下运动。这就是为什么我们冒险用30天窗口内数据的均值和方差(z-score标准化)来标准化的原因,不妨假设在一个时间窗口内均值和方差不会改变很多,和未来的信息也没有关系。

代码语言:javascript
复制
X = [(np.array(x) — np.mean(x)) / np.std(x) for x in X]

对于回归问题,我们已经不能像这样作弊了,所以我们用pandas计算收益率(价格与昨天相比变化的百分比),它看起来是这样的:

代码语言:javascript
复制
close_price_diffs = close.price.pct_change()

正如我们所看到的,这个数据已经比较规范了,位于-0.5到0.5之间。

神经网络结构

正如我之前所说,在本文中,我们将只使用MLPs来展示在金融数据上过度拟合神经网络是多么容易(在前一篇文章中实际上存在过拟合),以及如何预防它。在CNNs或RNNs上扩展这些想法相对容易,但是理解这个概念要重要得多。和以前一样,我们使用Keras作为神经网络原型的主要框架

我们的第一个网络是这样的:

代码语言:javascript
复制
model = Sequential() 
model.add(Dense(64, input_dim=30)) 
model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(2)) 
model.add(Activation('softmax'))

建议在每一个仿射或卷积层之后都使用批规范化方法和用 Leaky ReLU作为基本的激活函数,因为它已经成为了“工业标准”——它们有助于更快地训练网络。另一个好处是在训练中降低学习率,Keras用ReduceLROnPlateau函数做到了这一点:

代码语言:javascript
复制
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.9, 
patience=5, min_lr=0.000001, verbose=1) 

model.compile(optimizer=opt,                
loss='categorical_crossentropy', metrics=['accuracy'])

我们是这样开始训练的:

代码语言:javascript
复制
history = model.fit(X_train, Y_train, nb_epoch = 50,  
batch_size = 128, verbose=1, validation_data=(X_test, Y_test),           
shuffle=True, callbacks=[reduce_lr])

这是我们将结果可视化的方法(让我们来判断loss和accuracy图)

代码语言:javascript
复制
plt.figure() 
plt.plot(history.history['loss']) plt.plot(history.history['val_loss']) 
plt.title('model loss') 
plt.ylabel('loss') 
plt.xlabel('epoch') 
plt.legend(['train', 'test'], loc='best')

重要时刻 :

在上一篇文章中,我们在训练网络时只迭代了10次——这是完全错误的。即使我们能看到55%的准确率,这并不意味着我们能比随机猜测更好地预测未来。最有可能的是,在我们的训练数据集中有55%的窗口数据具有一种行为(向上),45%的窗口数据具有另一种行为(向下)。我们的网络只是学习到了训练数据的这种分布而已。所以,最好可以让网络迭代学习20-50-100次,如果迭代次数太多的话无法使用提前停止策略(early stopping)。

分类

First network loss

First network accuracy

结果一点都不好,我们的测试loss根本没有改变,我们可以看到明显的过拟合,让我们建立一个更深的网络并尝试一下:

代码语言:javascript
复制
model = Sequential() 
model.add(Dense(64, input_dim=30)) 
model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(16)) 
model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(2)) model.add(Activation('softmax'))

以下是结果:

Second network loss

Second network accuracy

在这里,我们看到结果或多或少是相同的,甚至更糟…… 是时候向模型添加一些正则化方法了,首先在权重求和上添加L2范数(译者注:一种深度学习中常用的减少泛化误差技术):

代码语言:javascript
复制
model = Sequential() 
model.add(Dense(64, input_dim=30,                 
activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(16,                 
activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(2)) 
model.add(Activation('softmax'))

它表现得更好,但仍然不够好(即使损失在减少,但准确性很差)。这种情况在处理金融数据时经常发生——在这里有更好的解释:

https://www.quora.com/Loss-cross-entropy-is-decreasing-but-accuracy-remains-the-same-while-training-convolutional-neural-networks-How-can-it-happen

Regularized network loss

Regularized network accuracy

接下来我要做的事情看起来很奇怪,就是我们要对已经正则化的网络进行正则化,以0.5的概率加入dropout(在反向传播时随机忽略一些权重,以避免神经元之间的共适应关系造成的过拟合):

代码语言:javascript
复制
model = Sequential()  
model.add(Dense(64, input_dim=30,                 
activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dropout(0.5)) 
model.add(Dense(16,                 
activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(2)) 
model.add(Activation('softmax'))

Hardcore regularized network loss

Hardcore regularized network accuracy

正如我们所看到的,图表上看起来有点凑合了,我们可以报告大约58%的准确率,这比随机猜测好一点。

回归

对于回归,我们将使用收益率数据和前面搭建好的神经网络(但没有dropout),并检查回归的效果如何:

代码语言:javascript
复制
model = Sequential()
model.add(Dense(64, input_dim=30,                 
activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(16,                 
activity_regularizer=regularizers.l2(0.01))) model.add(BatchNormalization()) 
model.add(LeakyReLU()) 
model.add(Dense(1)) 
model.add(Activation('linear'))

这是将预测结果可视化的代码:

代码语言:javascript
复制
pred = model.predict(np.array(X_test)) 
original = Y_test 
predicted = pred  
plt.plot(original, color='black', label = 'Original data') plt.plot(predicted, color='blue', label = 'Predicted data') plt.legend(loc='best') 
plt.title('Actual and predicted') 
plt.show()

它的效果很差,甚至不值得评论。在结论部分,我们将给出一些有助于解决回归问题的小提示。

回测

请想起通常我们为什么处理这些时间序列?我们想要建立一个交易系统,这意味着它必须做一些交易——买卖股票,希望能使投资组合增值。

有很多现成的解决方案可以测试你的策略(比如Quantopian),但我们决定学习它们是如何从内部构建的,这本详细介绍实现的方法:(文末下载

文末下载

附随书代码下载

我们测试的策略非常简单:如果我们的网络说股价会上涨,我们就买入股票,然后在网络说股价会下跌时卖出,并等待下一个买入信号。逻辑是这样的:

代码语言:javascript
复制
if np.argmax(pred) == 0 and not self.long_market:
     self.long_market = True
     signal = SignalEvent(1, sym, dt, 'LONG', 1.0)
     self.events.put(signal)
     print pred, 'LONG'
if np.argmax(pred) == 1 and self.long_market:
     self.long_market = False
     signal = SignalEvent(1, sym, dt, 'EXIT', 1.0)
     self.events.put(signal)
     print pred, 'EXIT'

以下是用2012 - 2016年数据训练的分类网络在2016 - 2017年5月测试的结果:

蓝色的图显示了组合净值的增长(哇,在1.5年里增长了3%),黑色的图显示了收益,红色的图-回撤(亏钱的时期)。

讨论

乍一看,结果很烂。糟心的回归和平淡无奇的分类(58%的准确率)似乎在乞求我们放弃这个想法。当你看到那“难以置信”的3%的收入(只买苹果股票并持有会更容易实现,在这段时间里品苹果股票增长了20%)之后,你也许想合上电脑,做一些不涉及金融或机器学习的事情。但是有很多方法可以改善我们的结果(人们在基金中所做的就是这些事情):

1、使用高频数据(每小时、分钟)——机器学习算法需要更多的数据,并且短期预测效果更好。

2、做一些超参数优化,不仅包括神经网络优化和训练参数优化,还包括训练的历史窗口优化。

3、使用更好的神经网络架构,如CNNs或RNNs。

4、不仅要使用收盘价或收益率,还可以使用每天的所有开高低成交量数据;如果可能的话——收集N个最相关的公司、行业财务状况、经济变量等信息。只依靠我们使用的简单数据是不可能建立好的预测模型的。

5、使用更复杂的,也许非对称的损失函数。例如,我们用于回归的MSE对符号是不变的,而符号却对我们的任务至关重要。

结论

预测金融数据是极其复杂的。很容易过度拟合,我们不知道要训练的正确历史范围,而且很难获得所需的所有数据。但正如我们所看到的,它是有效的,甚至可以带来一些利润。本文可以作为一个进一步研究的好起点并提供了探索的一个流程。

明天的文章中,我们计划展示自动化的超参数搜索过程,添加更多的数据(开高低收成交量量和财务指标),并使用强化学习来学习策略,以及检查强化学习的agent是否相信我们的预测。敬请期待!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 量化投资与机器学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档