专栏首页量化投资与机器学习神经网络在算法交易上的应用系列——时序预测+回测

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

本期作者:Alexandr Honchar

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

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

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

前言

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

数据准备

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

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标准化)来标准化的原因,不妨假设在一个时间窗口内均值和方差不会改变很多,和未来的信息也没有关系。

X = [(np.array(x) — np.mean(x)) / np.std(x) for x in X]

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

close_price_diffs = close.price.pct_change()

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

神经网络结构

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

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

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函数做到了这一点:

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'])

我们是这样开始训练的:

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图)

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根本没有改变,我们可以看到明显的过拟合,让我们建立一个更深的网络并尝试一下:

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范数(译者注:一种深度学习中常用的减少泛化误差技术):

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(在反向传播时随机忽略一些权重,以避免神经元之间的共适应关系造成的过拟合):

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),并检查回归的效果如何:

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'))

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

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),但我们决定学习它们是如何从内部构建的,这本详细介绍实现的方法:(文末下载

文末下载

附随书代码下载

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

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是否相信我们的预测。敬请期待!

本文分享自微信公众号 - 量化投资与机器学习(Lhtz_Jqxx)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 分隔百度百科中的名人信息与非名人信息

    像错误提示说的那样需要的是字节类型而不是字符串类型,需要注意一下的是bytes-like翻译为字节。

    明天依旧可好
  • Finale

    这几篇博客介绍的第一个feature transform方法就是kernel。kernel先出现是在SVM里面,原因就是为了减少 內积计算的复杂度,把特征转换和...

    西红柿炒鸡蛋
  • 放弃幻想,全面拥抱Transformer:自然语言处理三大特征抽取器(CNN/RNN/TF)比较

    在辞旧迎新的时刻,大家都在忙着回顾过去一年的成绩(或者在灶台前含泪数锅),并对2019做着规划,当然也有不少朋友执行力和工作效率比较高,直接把2018年初...

    beyondGuo
  • Random Forest

    随机森林还是没有脱离聚合模型这块,之前学过两个aggregation model,bagging和decision tree,一个是边learning边unif...

    西红柿炒鸡蛋
  • 基于Keras的DCGAN实现

    生成对抗网络(Generative Adversarial Network,简称GAN)是非监督式学习的一种方法,通过让两个神经网络相互博弈的方式进行学习。

    TheOneGIS
  • k-近邻算法

    从今天开始,与大家分享我学习《Machine Learning In Action》这本书的笔记与心得。我会将源码加以详细的注释,这是我自己学习的一个过程,也是...

    TheOneGIS
  • Learning ROS for Robotics Programming Second Edition学习笔记(一) indigo v-rep

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865

    zhangrelay
  • 机器学习第3天:多元线性回归

    简单线性回归:影响Y的因素唯一,只有一个。 多元线性回归:影响Y的因数不唯一,有多个。

    明天依旧可好
  • 大数据工程师需要学习哪些必备知识和技能呢?

    大数据这个行业在科学发展的潮流中也变得越来越火了,来带你看看大数据工程师需要学习哪些必备知识和技能呢?

    用户2292346
  • 第一章(1.2) 机器学习算法工程师技能树

    - 单机开发工具(numpy、sk-learn、pandas、libsvm、xgboost)

    两只橙

扫码关注云+社区

领取腾讯云代金券