深度学习在股票市场的应用
作者:chen_h
微信号 & QQ:862251340
微信公众号:coderpai
简书地址:http://www.jianshu.com/p/f9ca...
本文的代码请点击这里。Talk is cheap, show me the code.
在过去的几个月中,我对深度学习非常着迷,特别是它在自然语言处理中的应用。我的大部分职业生涯都是在金融领域度过的,特别是在算法交易和交易数据替换方面。
我写这篇文章的想法直接来自我的工作经验。我作为一名“深度学习”的爱好者时,对于深度学习我没有很深入的了解。但是我希望我的理解可以表达到位,并且可以帮助到你。
为什么股票预测与 NLP 存在关联性?
在很多的 NLP 问题中,我们最终会把序列编码成一个固定大小的表示,然后将这个编码再解码成另一个序列。例如,我们可能会在文本中进行实体标记,从英文翻译成法语,或将音频转换成文本。在这些领域都出现了大量的工作,并且很多成果都取得了最先进的表现。
在我看来,NLP 与金融数据分析之间的最大不同在于,语言有一定的结构性,虽然这个结构性比较模糊难懂。另一方面,金融市场数据可能是没有任何结构可以学习的。
在这个文章中,我们假设金融市场数据是存在结构性的,当然这个假设可能是不成立的,我们这个文章可能也会直接推翻这个假设。我们按照 NLP 的语义编码模型来类比这个市场结构,如果你觉得这个想法没有意义,那么请阅读下去,我相信你会觉得会有意义的。
词向量
词向量的模型有很多的,Richard Socher 的讲座是一个非常好的教程。简而言之,我们可以用语言模型来描述所有的单词,并且在坐标图中进行显示,并且利用几何图形来捕捉单词之间的关联性。有一个经典的例子是 “King - man + woman = Queen” ,或者类似别的例子。
词嵌入是非常酷的一个应用,因为我们可以利用一个很浓缩的方式来表示一个词信息。比较传统的词嵌入方法是构建一个包含所有单词的词表,如果当前词是查询词,那么该位置设置为 1 ,其余设置为 0 。但是这不是一个有效的方法,而且也没有任何词关联意义。但是通过词嵌入,我们用一个固定维度的向量来表示一个单词,然后利用更高维度的几何特性来寻找词之间的关联性。
下图展示了一个例子。词向量的模型是需要一个大型的语料库进行训练。经过几天的高密度训练,每个词都会得到一个高维度的词向量。这个“空间”向量会有类似于“距离”的概念,所以我们可以计算哪些词是互相接近的。该方法的作者举了一个例子(就是下图),用来计算跟 Frog 距离近的词。
但是除了词,我们对别的事物也可以进行高维度的编码,比如我们接下来要介绍的金融市场数据。
Market2Vec
第一个我听说的词嵌入算法是 word2vec 。我想在金融市场数据上面得到相同的效果,虽然我会使用一些比较不同于 word2vec 的算法。我的输入数据是 csv 格式的。第一类表示日期,另外的 4*1000 列对应于 1000 只股票的高低开仓价。那么,我们的输入向量就是 4000 维度的,这个维度太大了。所以,我们第一件事就是把这个高维度的向量压缩到一个低维度的向量,比如说 300 维度(因为我们喜欢这个电影 ^_^)。
如果你觉得把一个 4000 维度的向量压缩到 300 维度是一件很难的事,那么你就大错特错了。其实这很简单,我们只需要乘以一个矩阵,矩阵就相当于一个巨大的 excel 表格,每个单元格中都会有一个数字,并且不存在任何的格式。想象一下,我们有一个 4000 列和 300 行的 excel 表格,我们把这个表格与上面的 4000 维度向量进行相乘,那么我们就可以得到一个 300 维度的向量。这个就是最基础的线性代数知识,你肯定能明白。
那么,现在的难点就是如何去设置这个巨大的表格矩阵,“深度学习” 就是用在这个矩阵的初始化和更新上面。最终,这个电子表格矩阵将会是一个不错的转移矩阵,可以将我们的 4000 维度向量转换成一个简洁的 300 维度向量。
接下来,我们要做一些比较神奇的事,那就是激活函数。我们要给每一个值加上一个激活函数。将这个激活函数添加到每一个值上面,将每个值的大小归到 0 到 1 之间。那么,为什么这样做可以使得我们的向量更加特别,可以学习和理解更加复杂的事。你可以点击这里进行学习。
那么接下来我们需要处理的重要的事就是如何去找到这个转移矩阵,使得我们可以损失更少的信息,将高维的股票向量转换成低维的股票向量。我们希望能利用这个向量来挖掘各个股票之间的相关性,也许我们会发现当某个行业下滑时,另一个行业的股票也会下滑。当然,我们可能也会发现一些别的特征,我认为这对我们将来的研究会非常有用。
初步接触神经网络
让我们先把股票向量放在一边,来谈谈语言模型。Andrej Karpathy 写了一篇很流行的文章 The Unreasonable effectiveness of Recurrent Neural Networks 。对这篇文章可以简单总结如下:
- 如果我们学习莎士比亚的作品,然后一个字一个字的进行学习。那么我们可以深度学习来学习一种语言模型。
- 在这种情况下,语言模型是一个魔术盒。你可以提供一句话的前面几个字符,模型就会告诉你下一个字符是什么。
- 如果我们把语言模型的预测结果重新输入到语言模型,那么我们模型将永远运行下去。
然后这个模型会产生一大堆看起来像莎士比亚风格的文字。然后,我们也可以将这个思想用在 Linux 源码上面,然后程序来产生代码。或者,我们可以用在几何代数课本上面,让程序来产生内容。
这个魔术盒的实现机制我们会在后续进行介绍,但是让我们先来假想一下,Karpathy 是利用前面的一些字符来预测下一个字符,那么我们是否可以根据之前的市场向量,来预测下一个市场表现、市场向量或者别的东西,而这个预测模型就是这个魔术盒。我们还没有决定我们想要预测的是什么,但是没有关系,因为我们不会将我们的输出反馈到我们的输入中。
更加深入分析
我想指出,从现在开始我们要开始深入的去分析深度学习。到目前为止,我们只是使用单层网络进行学习,也就是只是使用我们上面设计的按个 4000 * 300 的转移矩阵。接下来,我们会添加更多的层次,来设计一个深层的神经网络来设计这个市场向量,这就是深度学习。
所以,Karpathy 向我们介绍了一些 Linux 例子的输出,下面的代码就是魔术盒的内容:
static void action_new_function(struct s_stat_info *wb) { unsigned long flags; int lel_idx_bit = e->edd, *sys & ~((unsigned long) *FIRST_COMPAT); buf[0] = 0xFFFFFFFF & (bit << 4); min(inc, slist->bytes); printk(KERN_WARNING "Memory allocated %02x/%02x, " "original MLL instead\n"), min(min(multi_run - s->len, max) * num_data_in), frame_pos, sz + first_seg); div_u64_w(val, inb_p); spin_unlock(&disk->queue_lock); mutex_unlock(&s->sock->mutex); mutex_unlock(&func->mutex); return disassemble(info->pending_bh); }
请注意,该魔术盒可以知道如何去打开和关闭括号,并且能遵守程序的缩进约定,也就是函数代码的正确缩进,多行 printk 语句也具有正确的内部缩进。这也就意味着这个魔术盒可以理解代码的远程依赖关系,即魔术盒可以联系到前面的代码进行分析推敲。当它预测在 print 语句中时,它会知道它在一个 print 语句中,并且会记住它是在一个函数中(或者至少记住是在另一个缩进范围)中。太好了,这就很容易理解,具有捕捉细节和记住长期依赖关系的算法是非常有用的,因为我们也是希望能在市场上找到长期的依赖关系。
深入探索魔术盒
那么这个神奇的魔术盒里面有什么呢?它是一种被称为 LSTM 的循环神经网络(RNN)。RNN 是一种深度学习算法,它擅长的是对序列进行操作(如字符序列)。在每一步,它对会对下一个字符进行预测,并且使用一个矩阵记性表示,就像我们之前看到的那样。因为 RNN 有一些内部的记忆,所以它可以记住一些以前看到的东西。它会使用这个内部记忆来决定下一次输入中应该如何操作。使用该内存记忆,RNN 可以记住它在一定范围内的内容,这就是我们为什么可以对输入文本进行预测处理。
RNN 有一个很好的变体称为 LSTM,LSTM 巧妙的设计了 RNN 内部的记忆单元,这个单元可以做以下事:
- 可以选择性的记住一些东西;
- 可以选择性的去忘记一些东西;
- 选择应该输出多少记忆内存;
所以,当一个 LSTM 看到一个 “{” ,并且对自己说“这个非常重要,我应该把它记住” 。而当它这样做时,它基本上就记住了这是一个嵌套的范围。一旦看到相应的 “}” ,它就会决定忘记原来的大括号,因此忘记它在一个嵌套的范围内。
我们可以将几个 LSTM 堆叠在一起,这样 LSTM 可以学习到更多的抽象概念。现在,前一个 LSTM 的输出变成了下一个 LSTM 的输入,每个 LSTM 会学习到更加抽象的数据。在上面的例子中(这个只是说明性的猜测解释),第一层的 LSTM 可能会学习由空格分割的字符,下一层可能会学习像(static void action_new_function)这样的字符类型,下一层可能会学习函数的概念以及参数等等。虽然 Karpathy 在博客中已经有非常好的说明例子了,但是要清楚的说明每一层的具体任务还是非常苦难的。
Market2Vec 与 LSTMs 的结合
你可能会注意到,Karpathy 使用字符作为他模型的输入,而不是词嵌入(技术上是使用 0-1 编码)。但是,Lars Eidnes 在 Auto-Generating Clickbait With Recurrent Neural Network 文章中使用词嵌入来处理。
上图显示了他使用的网络。我们先不考虑 softmax 部分(这部分我们后续介绍),先来看看 LSTM 堆栈和模型的输入部分。我们发现模型的底部输入是一系列的单词向量(记住,一个词向量表示的是一个单词的语义)。Lars 输入一个字向量序列,其中每一个作用如下:
- 影响第一个 LSTM;
- 前一个 LSTM 的中间输出作为下一个 LSTM 的输入;
- 前一个 LSTM 输出的下一个字作为下一个 LSTM 的输入;
虽然我们要做同样的事情,但是可能有一点小小的区别,我们不是把字向量作为输入,而是我们的 Market2Vec ,这个市场向量就是我们之前描述出来的。我们来回顾一下,对于给定的时间点,我们的 MarketVectors 应该包含在这个时间点发生的市场情况的总和。通过 LSTM 的序列化,我们希望能够捕捉到市场上发生的一个长期依赖关系。通过几个 LSTM 的堆栈,我们希望可以得到更高层次的市场行为抽象数据。
我们来谈谈股票
到目前为止,我们还没有谈到算法是如何在实际环境中工作的,我们上面只是谈论了如何对数据做巧妙的转换,把一个高维度的数据转换成低维度的数据。在接下来的文章中,我们会来具体说如何应用这个算法,但是请记住,正是因为前面的铺垫才使得后面的应用有了价值。
在 Karpathy 的例子中,LSTM 的输出是抽象的表示下一个字符的预测向量。在 Eidnes 的例子中,LSTM 是输出下一个字符向量。这两种情况的下一步都是将一个抽象的事物表示成一个概率向量,这是一个概率列表,说明每个字符或者单词在下一个位置出现的可能性。这里则是 softmax 函数在工作了,一旦我们拥有了这个概率列表,我们则需要选择出最有可能出现的单词或者字符。
当我们处理“市场预测”的问题时,我们需要问自己,我们真正想要预测的是市场的什么情况?我这里有一些自己的想法,大家可以参考一下:
- 预测 1000 支股票中每一支股票接下来的价格变化;
- 预测在接下来的 n 分钟内,一些指标(S&P,VIX等等)的变化;
- 预测在接下来的 n 分钟内,哪些股票的价格将上涨超过 x%;
- (我个人最喜欢的)预测哪些股票在接下来的 n 分钟内,上涨/下跌 2x%,而这段时间内不会下跌/上涨超过 x% ;
- (本文将会介绍的)预测在接下来的 n 分钟,VIX 的值上涨/下跌 2x %,而这段时间内不会下跌/上涨超过 x%;
第一和第二个就是回归问题,我们必须预测的是一个实际的数字而不是一个特定事件的概率(比如字符 n 出现或者市场上涨)。这个非常准确,但是不是我想做的事。
第三和第四是两个非常相似的问题,他们都要求预测一个事件(技术术语,就是一个类标签)。这个事件可能是接下来出现字母是 n ,或者接下来10分钟内某只股票是否会上升 5%,而不会下降超过 3%。第三和第四之间的权衡关系是,第三个问题更加常见,因此更容易学习到,而第四个问题更加有价值,它不但对利润有预测,而且对风险也有一定的约束。
第五个问题是我们在这篇文章需要解决的,这个问题跟第三个和第四个问题非常类似,但是它更加容易一些,因为我们有一些机制可以遵循。VIX 指数被称为恐怖指数,它代表了 S&P 500 指数的股票波动。它是通过观察指数中每个股票的隐含波动率得出来的。
为什么预测 VIX 指数?
那么我们为什么会去预测 VIX 这个指数呢?原因主要如下:
- 这只是一个数字,而不是 1000 支股票,这就使得我们在算法设计上会更加简单,计算成本也会降低很多;
- 这是多个股票的总结,所以跟大多数股票是相关的;
- 它不是单纯的对我们的输入数据进行线性组合。隐含的波动率是从一个非常复杂的非线性公式中计算出来的,而 VIX 是基于这个波动率再进行推导得出来的。如果我们可以进行预测,那么这将是一件非常酷的事。
- 这个是直接可以用来指导交易的额,如果我们真的可以设计出一个有价值的网络,那么我们就可以直接在线上使用它。
继续讨论 LSTM 和 softmax 函数
在未来几分钟内,我们如何使用之前的数据来预测 VIX 的变化呢?对于我们数据集中的每一个点,我会在 5 分钟后再次看到该数据点的 VIX 变化。如果在那段时间内上升了超过 1%,但是没有超过 0.5%。那么,我们的模型将输出 1,否则就是输出 0。然后我们会得到如下一系列的标签:
0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0 ….
我们想利用之前压缩的市场向量,然后输入到 LSTM 模型中得出最终的概率结果,从技术上来分析,因为我们最终只要输出一个标签,我们也可以使用 sigmoid 函数,但是如果我们做二分类结果,还是建议使用 softmax 函数。
所以,在我们深入分析这个算法应用之前,让我们先来回顾一下我们已经学习了哪些内容:
- 我们以 1000 支股票的价格作为输入数据;
- 数据序列中的每一个时间点都是市场的一个快照反馈。我们使用一个压缩网络,将输入的 4000 维度压缩到 300 维度;
- 现在我们有一系列的股票市场向量,我们把这些股票向量输入堆栈 LSTM 中,然后按照每个时间步骤来进行学习。LSTM 会记住一些市场之前的有效信息,这些信息会影响后续的市场反馈;
- 我们将第一层 LSTM 的输出传递到另外的 LSTM 结构。这些结构会记住一些更高层次的抽象信息;
- 最后,我们会根据堆栈 LSTM 的最后输出,利用 softmax 函数来得到VIX 在接下来5分钟内上涨 1%,而不下降 0.5%的具体股票;
这个过程是如何选择的?
现在是最有趣的部分。我们现在所做的一切都可以被称为是一个前向的过程。当我们训练算法时,我们也会去做以上的那些步骤,然后部署到生产环境中使用它。现在我们需要讨论的是一个反馈的过程,这个反馈是我们需要在算法中进行学习的过程。
在训练的过程中,我们不仅准备了数年的历史训练数据,还手动准备了一系列的预测目标,0 和 1 都是我们对历史数据进行打的标签。
为了学习,我们需要将市场数据输入我们的神经网络,并且将网络的输出结果和我们的标签数据进行对比,最后得到一个损失值,这个对比函数就是一个损失函数。在我们的例子中这个对比比较简单,我们可以将我们的损失函数设计如下:
ERROR = (1/2)*(((precomputed)— (predicted probability))² )
这就是一个损失函数。也就是说,我们可以去计算出深度学习模型的输出值与准确值之间的误差有多大。根据这些误差,我们可以利用一些更新函数来更新一整个网络的权重,从而得到一个比较好的模型。
该更新过程会一直更新到模型的开始,所以模型会不断的调整我们的 MarketVectors 向量,以便我们的 MarketVectors 可以表示更多的信息。
LSTM 模型可以选择去记住哪些内容和忘记哪些内容,以便模型可以分析那些与我们任务最相关的量。
LSTM 模型还可以学习数据的更加抽象表示,以便模型学习数据更多的特征。
这一点是非常让我吃惊的,因为深度学习可以学习那么多负责和抽象的东西。我们从前都没有接触过这种模型。
一些想法
对于这个项目我还有一些更加深入的想法,一些我可能还会尝试的想法,并且我认为这些想法对实际项目时有用的。
市场流动性和资金的有效利用
一般来说,特定市场的流动性越高资金的利用效率越高。我认为这是一个鸡与蛋之前的循环,而市场变得更加的流动,它能够吸收更多的资本交易,并且资本收到的损害最小。随着市场变得更加流动,更多的资本可以在市场中使用,你会发现更多复杂的交易玩家会进入市场。
一个快速的推论是,在一个比较不流动的市场中,竞争并不是那么复杂,所以这样的机制可能会带来机会,我会在这样的市场尝试交易。
股票市场的新应用
这些算法的知识和架构对计算机领域是比较陈旧的,但是在股票金融领域的应用是一个全新的应用。我们假设一些顶级玩家已经在使用这些技术进行套利,并且可能已经很长一段时间了。但是正如我前面提到的,他们可能是在一个流动性很大的市场进行套利工作,他们需要很大的市场资金规模,这样才能去平摊他们的运营成本。我认为,下一次的市场应用会在那些流动性比较小的市场,这些市场还没有被开发完全,存在很大的套利空间。
多个时间帧
虽然我在上面提到的是输入一个单通道的数据流,但我认为一个更加有效的训练方法是在多个时间通道上面对市场数据进行训练。也就是说,我目前采用的是 30 秒采样一次,我希望网络可以解决几个小时的数据依赖关系。
我不知道这个想法是否有意义,但是我认为如果我们可以将计算成本降低到一定程度,那么我们可以把多个时间点进行融合训练。这个融合模型的训练我还在思考中,目前有点困难。
MarketVectors
当我们在自然语言处理中,使用的词向量模型都是在一个预训练模型之上进行微调的。但是在我们的情况下,没有一个预训练好的市场向量可以使用,也没有一个明确的算法去进行训练。
我原来的想法是使用一个自动编码器来进行训练,就好像这篇论文,但是端到端的训练好像看起来更加酷。
更重要的考虑是,端到端的序列模型在机器翻译领域已经比较成熟了,也就是说我们可以把一些文本编码成一个向量,并且进行解码。在这种观点下,我所描述的整个架构本质上就是一个编码器,但是我还没有真正的设计出一个解码器。
但是,对于网络的第一层,我想设计一个特殊层,即我们需要把输入的 4000 维度向量降维到一个 300 维度的向量。通过这些低维度的向量,我们需要去找到各个股票之间的一些关联度。
另一个替代的方向是对每个 LSTM 输入一个数据,然后编码成一个输出向量。但我认为这个可能是无效的,因为各个数据模型之间的相关度会下降很多,并且这种方法会需要 10 倍的计算资源,比较浪费。但是从另一方面来说,这种架构可以多个 GPU 进行训练,可以提高一点速度。
CNNs
最近,字符级的机器翻译方面出现了很多的论文。这篇论文带给了我很多的启发,因为这篇文章试图采用 CNN 来解决长距离的依赖关系,而不是 RNN 。我还没有深入的去阅读这篇论文,但是我们可以假想一下,我们把每个股票当做一个通道(类似于 RGB图像的三个通道),然后把每个通道输入卷积模型中进行卷积操作,从而来捕捉市场的长期依赖信息。这种方法跟他们从字符级别上进行语义编码的方式是相同的。
算法社区直播课:请点击这里
作者:chen_h
微信号 & QQ:862251340
简书地址:http://www.jianshu.com/p/f9ca...
CoderPai 是一个专注于算法实战的平台,从基础的算法到人工智能算法都有设计。如果你对算法实战感兴趣,请快快关注我们吧。加入AI实战微信群,AI实战QQ群,ACM算法微信群,ACM算法QQ群。长按或者扫描如下二维码,关注 “CoderPai” 微信号(coderpai)