机器学习算法交易教程第二版(一)

原文:zh.annas-archive.org/md5/a3861c820dde5bc35d4f0200e43cd519

译者:飞龙

协议:CC BY-NC-SA 4.0

序言

如果你正在阅读这篇文章,你可能已经意识到机器学习ML已经成为许多行业的战略能力,包括投资行业。与 ML 的兴起密切相关的数字数据的爆炸对投资产生了特别强大的影响,而投资已经有了使用复杂模型处理信息的悠久历史。这些趋势正在促成量化投资的新方法,并增加了将数据科学应用于自主和算法交易策略的需求。

跨资产类别的交易范围非常广泛,因为它涵盖了从股票和政府债券到商品和房地产的范围。这意味着一个非常广泛的新型替代数据源可能与过去大多数分析工作的核心市场和基本数据相关。

你可能也已经了解到,成功应用 ML 或数据科学需要个人或团队层面的统计知识、计算技能和领域专业知识的整合。换句话说,关键是提出正确的问题,识别和理解可能提供答案的数据,使用广泛的工具来获取结果,并以导致正确决策的方式解释它们。

因此,本书提供了关于将 ML 应用于投资和交易领域的综合视角。在本序言中,我们概述了你应该期望什么,我们如何组织内容以便实现我们的目标,以及你需要什么来实现目标并在过程中享受乐趣。

预期

本书旨在为您提供战略视角、概念理解和实用工具,以在将 ML 应用于交易和投资过程中增加价值。为此,我们将 ML 视为过程的关键要素,而不是一个独立的练习。最重要的是,我们介绍了一个端到端 ML 交易(ML4T)工作流程,我们将其应用于许多具有相关数据和代码示例的用例。

ML4T 的工作流程始于生成想法和获取数据,然后继续提取特征,调整 ML 模型,并设计能够根据模型预测信号行动的交易策略。它还包括使用回测引擎在历史数据上模拟策略,并评估其性能。

首先,本书演示了如何从各种数据源中提取信号,并使用广泛的监督、无监督和强化学习算法为不同资产类别设计交易策略。此外,它提供了相关的数学和统计背景,以便调整算法和解释结果。最后,它包括金融背景,使您能够处理市场和基本数据,提取有信息量的特征,并管理交易策略的性能。

本书强调,投资者可以从第三方数据中获得至少与其他行业同等的价值。因此,它不仅涵盖了如何处理市场和基本数据,还涵盖了如何获取、评估、处理和建模替代数据源,如非结构化文本和图像数据。

毫不奇怪,这本书不提供投资建议或现成的交易算法。相反,它意在传达机器学习在交易领域面临许多额外挑战,从信号内容较低到时间序列较短,这些往往使得获得稳健结果更加困难。事实上,我们包含了一些例子,这些例子并没有取得很好的结果,以避免夸大机器学习的好处或低估获取好主意、获得正确数据、构建巧妙特征和设计有效策略(可能具有吸引力回报)所需的努力。

相反,您应该将本书视为一个利用关键机器学习算法指导交易策略的指南。为此,我们提出了一个框架,指导您完成以下 ML4T 过程

  1. 为任何投资目标获取、评估和组合数据

  2. 设计和调整从数据中提取预测信号的 ML 模型

  3. 基于结果开发和评估交易策略

阅读本书后,您将能够开始设计和评估自己基于机器学习的策略,并可能考虑参加比赛或连接到在线经纪人的 API 并开始在真实世界中交易。

第二版的新内容

本次更新的重点在于端到端的 ML4T 工作流程,反映在一个新的章节上,即战略回测(第八章ML4T 工作流程 - 从模型到策略回测),一个描述了超过 100 种不同的 alpha 因子的新附录,以及许多新的实际应用。我们还重写了大部分现有内容,以提高清晰度和可读性。

应用现在使用了更广泛的数据源,包括日常美国股票价格之外的国际股票和 ETF,以及分钟级频率的股票数据,以展示日内交易策略。此外,现在还有更广泛的替代数据源涵盖范围,包括用于情感分析和回报预测的 SEC 文件,以及用于分类土地用途的卫星图像。

此外,本书复制了几篇最近发表的学术论文中的应用。第十八章用于金融时间序列和卫星图像的卷积神经网络,演示了如何将卷积神经网络应用于转换为图像格式的时间序列,用于回报预测。第二十章用于条件风险因素和资产定价的自编码器,展示了如何使用自编码器提取以股票特征为条件的风险因素,用于资产定价。第二十一章用于合成时间序列数据的生成对抗网络,研究了如何使用生成对抗网络创建合成训练数据。

所有应用现在都使用最新可用的软件版本(写作时),如 pandas 1.0 和 TensorFlow 2.2。还有一个定制版本的 Zipline,可以在设计交易策略时轻松包含机器学习模型的预测。

谁应该阅读这本书

如果你是分析师、数据科学家或机器学习工程师,对金融市场有一定的了解并对交易策略感兴趣,那么你应该会觉得这本书很有启发。如果你是一名投资专业人士,希望利用机器学习做出更好的决策,你也会发现其中的价值。

如果你的背景是软件和机器学习,你可能可以只浏览或跳过一些本领域的介绍性材料。同样,如果你的专业是投资,你可能已经熟悉我们为不同背景的人提供的一些或全部金融背景。

本书假设你想继续学习这个非常动态的领域。为此,它包括了许多章末的学术参考文献,以及伴随 GitHub 仓库中每章的 README 文件中链接的其他资源。

你应该能够熟练使用 Python 3 和科学计算库,如 NumPy、pandas 或 SciPy,并期待在学习过程中掌握更多其他技能。一些机器学习和 scikit-learn 的经验会有所帮助,但我们会简要介绍基本的工作流程,并引用各种资源来填补空白或深入了解。同样,基本的金融和投资知识会让一些术语更容易理解。

本书涵盖了什么

本书全面介绍了机器学习如何为交易策略的设计和执行增加价值。它分为四个部分,涵盖了数据获取和策略开发过程的不同方面,以及解决各种机器学习挑战的不同解决方案。

第一部分 - 数据、Alpha 因子和投资组合

第一部分涵盖了在利用机器学习的交易策略中相关的基本方面。它专注于驱动本书讨论的 ML 算法和策略的数据,概述了如何构建捕捉数据信号内容的特征,并解释了如何优化和评估投资组合的绩效。

第一章交易的机器学习 - 从构想到执行,总结了 ML 如何以及为什么对于交易变得重要,描述了投资过程,并概述了 ML 如何增加价值。

第二章市场和基本数据 - 来源和技术,涵盖了如何获取和处理市场数据,包括交易所提供的 tick 数据和报告的财务数据。它还演示了许多开源数据提供商的访问,这些提供商在本书中将是我们依赖的。

第三章金融替代数据 - 类别和用例,解释了评估爆炸式增长的来源和提供商的类别和标准。它还演示了如何通过网页抓取创建替代数据集,例如,收集用于与自然语言处理NLP)和情感分析一起使用的收益电话转录,在本书的第二部分中我们将介绍。

第四章金融特征工程 - 如何研究阿尔法因子,介绍了创建和评估捕捉预测信号的数据转换过程,并展示了如何衡量因子绩效。它还总结了对旨在解释被认为是高效的金融市场中阿尔法的风险因素研究的见解。此外,它演示了如何使用 Python 库离线工程阿尔法因子,并介绍了ZiplineAlphalens库来回测因子并评估其预测能力。

第五章投资组合优化与绩效评估,介绍了如何管理、优化和评估由策略执行产生的投资组合。它提供了风险度量标准,并展示了如何使用 Zipline 和pyfolio库应用它们。它还介绍了从投资组合风险角度优化策略的方法

第二部分 - 用于交易的 ML - 基础知识

第二部分阐明了在端到端工作流程背景下,基本的监督和无监督学习算法如何指导交易策略。

第六章机器学习过程,通过概述如何系统地制定、训练、调整和评估 ML 模型的预测性能,为舞台设置了背景。它还解决了特定领域的问题,例如使用与金融时间序列交叉验证以在备选 ML 模型中进行选择。

第七章线性模型 - 从风险因子到收益预测,展示了如何使用线性和 logistic 回归进行推断和预测,以及如何使用正则化来管理过拟合的风险。它演示了如何预测美国股票的收益或其未来走势的方向,并使用 Alphalens 评估这些预测的信号内容。

第八章ML4T 工作流程 - 从模型到策略回测,将迄今为止单独讨论过的 ML4T 工作流程的各个组成部分集成在一起。它从端到端的视角展示了设计、模拟和评估由 ML 算法驱动的交易策略的过程。为此,它演示了如何使用 Python 库 backtrader 和 Zipline 在历史市场环境中进行 回测 ML 驱动的策略

第九章用于波动率预测和统计套利的时间序列模型,涵盖了单变量和多变量时间序列诊断和模型,包括向量自回归模型以及用于波动率预测的 ARCH/GARCH 模型。它还介绍了协整,并演示了如何使用它进行 使用各种交易所交易基金 (ETFs) 的配对交易策略

第十章贝叶斯 ML - 动态夏普比率和配对交易,介绍了概率模型以及如何使用 马尔可夫链蒙特卡洛 (MCMC) 采样和变分贝叶斯进行近似推断。它还说明了如何使用 PyMC3 进行概率编程,以更深入地了解 参数和模型不确定性,例如在评估 投资组合绩效 时。

第十一章随机森林 - 用于日本股票的多头-空头策略,展示了如何构建、训练和调整非线性基于树的模型进行洞察和预测。它介绍了基于树的集成,并展示了随机森林如何使用自助聚合来克服决策树的一些弱点。然后,我们继续开发和回测 用于日本股票的多头-空头策略

第十二章提升您的交易策略,介绍了梯度提升,并演示了如何使用库 XGBoost、LightBGM 和 CatBoost 进行高性能训练和预测。它回顾了如何调整众多超参数并解释模型使用 SHapley Additive exPlanation (SHAP) 值之前,基于 LightGBM 收益预测构建和评估交易美国股票的策略。

第十三章基于数据驱动的风险因子和无监督学习的资产配置,展示了如何使用降维和聚类进行算法交易。它使用主成分和独立成分分析提取数据驱动的风险因子并生成 特征组合。它介绍了几种聚类技术,并演示了使用分层聚类进行 资产配置

第三部分 - 自然语言处理

第三部分专注于文本数据,并介绍了从这一关键替代数据源提取高质量信号的最新无监督学习技术。

第十四章用于交易的文本数据 - 情绪分析,演示了如何将文本数据转换为数字格式,并将第二部分的分类算法应用于大型数据集的情绪分析。

第十五章主题建模 - 摘要财经新闻,使用无监督学习来提取总结大量文档的主题,并提供更有效地探索文本数据或将主题用作分类模型特征的方法。它演示了如何将这种技术应用于第三章中获取的收益电话转录和提交给美国证券交易委员会SEC)的年度报告。

第十六章用于收益电话和 SEC 提交文件的词嵌入,使用神经网络学习最先进的语言特征,以词向量的形式捕捉语义上下文,比传统文本特征更好地表示,并且代表从文本数据中提取交易信号的一个非常有前途的途径。

第四部分 - 深度学习和强化学习

第四部分介绍了深度学习和强化学习。

第十七章交易的深度学习,介绍了 TensorFlow 2 和 PyTorch,这两个最流行的深度学习框架,我们将在第四部分中使用。它提供了训练和调整的技巧,包括正则化。它还构建并评估了一种美国股票的交易策略

第十八章用于金融时间序列和卫星图像的卷积神经网络,涵盖了非常适用于大规模非结构化数据分类任务的卷积神经网络CNNs)。我们将介绍成功的架构设计,训练一个 CNN 模型来处理卫星数据(例如,预测经济活动),并使用迁移学习加速训练。我们还将复制一个最近的想法,将金融时间序列转换为二维图像格式,以利用 CNNs 的内置假设。

第十九章用于多变量时间序列和情感分析的循环神经网络,展示了循环神经网络RNNs)在序列到序列建模中的用处,包括用于预测的单变量和多变量时间序列。它演示了如何使用第十六章介绍的词嵌入来捕捉长期内的非线性模式,以根据 SEC 提交的文件中表达的情绪来预测回报

第二十章用于条件风险因素和资产定价的自编码器,涵盖了用于高维数据的非线性压缩的自编码器。它实现了一篇最近的论文,该论文使用深度自编码器从数据中学习风险因素回报和因子加载,同时将后者条件化为资产特征。我们将创建一个包含元数据的大型美国股票数据集,并生成预测信号。

第二十一章用于合成时间序列数据的生成对抗网络,介绍了深度学习中最激动人心的进展之一。生成对抗网络GANs)能够学习生成目标数据类型的合成副本,例如名人的图像。除了图像外,GANs 还被应用于时间序列数据。本章复制了一种生成合成股价数据的新方法,这些数据可用于训练 ML 模型或回测策略,并评估其质量。

第二十二章深度强化学习 - 构建交易代理,介绍了强化学习RL)如何允许设计和训练代理程序,这些程序会随着时间的推移而学会如何在环境变化中优化决策。您将看到如何创建自定义交易环境,并使用 OpenAI Gym 构建一个根据市场信号做出响应的代理。

第二十三章总结与下一步,总结了所学到的教训,并概述了您可以采取的几个步骤,继续学习并构建自己的交易策略。

附录Alpha 因子库,列出了近 200 个流行的金融特征,解释了它们的基本原理,并展示了如何计算它们。它还评估并比较了它们在预测每日股票收益方面的表现。

获取本书最大的收益

除了前一节总结的内容外,本书的实践性质还包括托管在 GitHub 上的超过 160 个 Jupyter 笔记本,演示了如何在广泛的数据源上将 ML 用于实践交易。本节描述了如何使用 GitHub 存储库,获取许多示例中使用的数据,并设置运行代码的环境。

GitHub 存储库

本书围绕将 ML 算法应用于交易展开。实践方面在 Jupyter 笔记本中进行,这些笔记本托管在 GitHub 上,详细说明了许多概念和模型。虽然各章节旨在是独立的,但代码示例和结果通常需要太多空间才能完整地包含在其中。因此,在阅读每章时,查看包含重要额外内容的笔记本非常重要,即使您不打算自己运行代码。

存储库组织良好,每个章节都有自己的目录,其中包含相关的笔记本和一个包含必要的单独说明的 README 文件,以及与该章节内容相关的参考和资源。在必要时,会在每章节中标识出相关的笔记本。存储库还包含有关如何安装必需库和获取数据的说明。

您可以在以下地址找到代码文件:github.com/PacktPublishing/Machine-Learning-for-Algorithmic-Trading-Second-Edition

数据来源

我们将使用来自市场、基本和替代来源的免费可用历史数据。第二章第三章 讨论了这些数据源的特点和访问方式,并介绍了本书中将使用的一些主要提供者。刚才描述的配套 GitHub 仓库包含了获取或创建本书中将使用的一些数据集的说明,其中包括一些较小的数据集。

我们将要使用的一些样本数据源包括但不限于:

  • 纳斯达克 ITCH 挂单数据

  • 电子数据获取、分析和检索 (EDGAR) 美国证监会文件

  • 来自 Seeking Alpha 的收益电话转录

  • Quandl 每日价格和其他超过 3,000 家美国公司的数据点

  • 来自 Stooq 的国际股票数据,并使用 yfinance 库

  • 来自美国联邦储备系统的各种宏观基本和基准数据

  • 大型 Yelp 商家评论和 Twitter 数据集

  • EUROSAT 卫星图像数据

一些数据很大(几个吉字节),例如纳斯达克和美国证监会文件。笔记本会在这种情况下指出。

请查看 GitHub 仓库根目录中的数据目录以获取说明。

Anaconda 和 Docker 镜像

本书要求使用 Python 3.7 或更高版本,并使用 Anaconda 发行版。本书使用各种 conda 环境来覆盖四个部分,涵盖了广泛的库,同时限制了依赖关系和冲突。

GitHub 仓库中的安装目录包含了详细的说明。您可以使用提供的 Docker 镜像创建具有所需环境的容器,也可以使用 .yml 文件在本地创建。

下载示例代码文件

您可以从您在www.packtpub.com上的账户下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support,注册后文件将直接发送至您的邮箱。

您可以按照以下步骤下载代码文件:

  1. www.packtpub.com上登录或注册。

  2. 选择SUPPORT选项卡。

  3. 点击代码下载和勘误

  4. 搜索框中输入书名,然后按照屏幕上的说明操作。

下载文件后,请确保使用您首选的最新版本的压缩工具解压缩或提取文件夹:

  • Windows 上的 WinRAR 或 7-Zip

  • Mac 上的 Zipeg、iZip 或 UnRarX

  • Linux 上的 7-Zip 或 PeaZip

本书的代码捆绑包也托管在 GitHub 上,网址为github.com/PacktPublishing/Machine-Learning-for-Algorithmic-Trading-Second-Edition。我们的丰富书籍和视频目录中还有其他代码捆绑包,可以在github.com/PacktPublishing/上找到。欢迎查看!

下载彩色图片

我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图像。您可以在此处下载:static.packt-cdn.com/downloads/9781839217715_ColorImages.pdf

使用的约定

本书中使用了许多文本约定。

CodeInText:表示文本中的代码词,数据库表名,文件夹名,文件名,文件扩展名,路径名,虚拟 URL,用户输入和 Twitter 句柄。例如,“compute_factors() 方法创建一个 MeanReversion 因子实例,并创建长、短和排名管道列。”

代码块设置如下:

from pykalman import KalmanFilter
kf = KalmanFilter(transition_matrices = [1],
                  observation_matrices = [1],
                  initial_state_mean = 0,
                  initial_state_covariance = 1,
                  observation_covariance=1,
                  transition_covariance=.01) 

粗体:表示一个新术语,一个重要单词,或者您在屏幕上看到的单词,例如菜单或对话框中的单词,也会出现在文本中。例如,“Python Algorithmic Trading Library (PyAlgoTrade) 专注于回测,并支持模拟交易和实盘交易。”

信息提示以这种方式出现。

第一章:从构想到执行的交易机器学习

算法交易依赖于执行算法的计算机程序,以自动化交易策略的一部分或全部元素。算法是设计用于实现目标的一系列步骤或规则。它们可以采用许多形式,并促进了整个投资过程的优化,从构想生成到资产配置、交易执行和风险管理。

机器学习ML)涉及从数据中学习规则或模式的算法,以实现最小化预测误差等目标。本书中的示例将说明 ML 算法如何从数据中提取信息,以支持或自动执行关键的投资活动。这些活动包括观察市场和分析数据,形成对未来的期望并决定是否放置买入或卖出订单,以及管理由此产生的投资组合,以产生相对于风险的吸引人的回报。

最终,主动投资管理的目标是产生 alpha,即超过用于评估的基准的投资组合回报。主动管理的基本法则假定产生 alpha 的关键是准确的回报预测与对这些预测采取行动的能力(Grinold 1989; Grinold and Kahn 2000)。

该法则将信息比率IR)定义为表示主动管理价值的比率,即投资组合与基准之间的回报差异与这些回报的波动性的比率。它进一步将 IR 近似为以下乘积:

  • 信息系数IC),它以与结果的等级相关性来衡量预测的质量

  • 一种策略的广度表示为对这些预测的独立投注数量的平方根

金融市场上的高级投资者竞争意味着,要产生 alpha 并进行精确预测,需要优越的信息,无论是通过获取更好的数据、优越的处理能力,还是两者兼而有之。

这就是 ML 的作用所在:用于交易的机器学习ML4T)应用通常旨在更有效地利用迅速多样化的数据范围,以产生更好且更具操作性的预测,从而提高投资决策和结果的质量。

历史上,算法交易曾被更狭义地定义为将交易执行自动化以最小化卖方提供的成本。本书采用了更全面的视角,因为算法的使用以及特别是 ML 的使用已经影响到更广泛的活动范围,从生成想法和从数据中提取信号到资产配置、头寸规模和测试和评估策略。

本章探讨了引领机器学习成为投资行业竞争优势来源的行业趋势。我们还将研究机器学习在投资过程中的定位,以实现算法交易策略。具体而言,我们将涵盖以下主题:

  • 促使机器学习在投资行业崛起的关键趋势

  • 利用机器学习设计和执行交易策略

  • 交易中机器学习的流行用例

您可以在 GitHub 存储库的本章 README 文件中找到其他资源和参考资料的链接(github.com/PacktPublishing/Machine-Learning-for-Algorithmic-Trading-Second-Edition)。

机器学习在投资行业的崛起

在过去几十年里,投资行业发生了巨大变革,并在竞争加剧、技术进步和具有挑战性的经济环境中继续发展。本节回顾了塑造整体投资环境和算法交易及机器学习使用的关键趋势。

推动算法交易和机器学习走向目前突出地位的趋势包括:

  • 市场微观结构的变化,例如电子交易的普及和跨资产类别和地理区域的市场整合

  • 风险因子暴露为框架制定的投资策略的发展,而不是资产类别

  • 计算能力数据生成与管理以及统计方法革命,包括深度学习的突破

  • 算法交易的先驱相对于人类、自主投资者的超额表现

此外,2001 年和 2008 年的金融危机影响了投资者对分散化和风险管理的态度。其中一个结果是被动投资工具的兴起,即交易所交易基金(ETF)形式的低成本被动投资工具

在 2008 年危机引发的低收益和低波动性背景下,领先央行大规模购买资产,注重成本的投资者将超过 3.5 万亿美元从主动管理的共同基金转移到被动管理的 ETF。

竞争压力还体现在对冲基金费用的降低,这些费用从传统的年管理费 2%和利润提取 20%降至 2017 年分别为平均 1.48%和 17.4%。

从电子交易到高频交易

自 20 世纪 60 年代网络开始将价格路由到计算机终端以来,电子交易在能力、交易量、资产类别覆盖范围和地理范围方面取得了巨大进步。股票市场在全球范围内一直处于这一趋势的前沿。有关金融市场相关变化的全面报道,请参阅 Harris(2003 年)和 Strumeyer(2017 年);在下一章中,我们将回顾如何处理市场和基本数据时再次讨论这个话题。

1997 年,SEC 发布的订单处理规则通过电子通讯网络ECNs)向交易所引入了竞争。 ECN 是自动化的替代交易系统ATS),可以按指定价格匹配买卖订单,主要用于股票和货币交易,并注册为经纪人。它允许不同地理位置的重要经纪公司和个人交易者直接进行交易,无需中介,无论是在交易所内还是在交易所外的交易时间。

暗池是另一种私人 ATS,允许机构投资者交易大额订单,而不公开其信息,与交易所在 ECN 竞争之前管理其订单簿的方式相反。暗池不公布交易前的买入和卖出报价,交易价格仅在执行后一段时间后才变为公开。自 2000 年代中期以来,它们已大幅增长,占美国股票交易的 40%,原因是对大额订单不利价格波动和高频交易者操纵订单的担忧。它们通常设在大型银行内,并受 SEC 监管。

随着电子交易的兴起,成本有效执行的算法迅速发展,并且从卖方向买方和跨资产类别迅速传播。自 2000 年左右,自动交易作为一种旨在实现成本有效执行的卖方工具出现,将订单分解为较小的、序列化的块,以限制其市场影响。这些工具传播到买方,并且通过考虑交易成本和流动性、以及短期价格和交易量预测等因素而变得越来越复杂。

直接市场访问DMA)通过允许交易者使用交易所成员经纪人的基础设施和市场参与者身份直接向交易所发送订单,从而使交易者对执行拥有更大的控制权。赞助访问通过经纪人移除了交易前的风险控制,并形成了高频交易HFT)的基础。

HFT 是指金融工具中以极低的延迟在微秒范围内执行的自动化交易,并且参与者持有非常短的持仓时间。其目标是检测和利用市场微观结构的效率低下,即交易场所的制度基础设施。

在过去的 10 年里,HFT 已大幅增长,并估计在美国股市交易量中占约 55%,在欧洲股市交易量中占约 40%。HFT 在期货市场也已大幅增长,占外汇期货交易量的大约 80%,以及利率期货和国债 10 年期期货交易量的三分之二(Miller 2016)。

HFT(高频交易)策略旨在利用被动或主动策略每次交易赚取小额利润。被动策略包括套利交易,以从不同交易场所交易的同一资产或其衍生品的极小价格差异中获利。主动策略包括订单预测或动量点火。订单预测,也称为流动性检测,涉及提交小型探索性订单的算法,以检测大型机构投资者的隐藏流动性,并在大订单之前进行交易,从随后的价格变动中获利。动量点火意味着一个算法执行并取消一系列订单,以欺骗其他 HFT 算法更积极地买入(或卖出)并从结果的价格变化中获利。

监管机构对某些激进的 HFT 策略与市场脆弱性和波动性增加之间的潜在联系表示关注,例如 2010 年 5 月的闪电崩盘、2014 年 10 月的国库市场波动以及 2015 年 8 月 24 日道琼斯工业平均指数暴跌超过 1000 点。与此同时,由于 HFT 的存在,市场流动性随着交易量的增加而增加,这降低了整体交易成本。

交易量减少、波动性降低以及技术成本和获取数据和交易场所的访问成本上升的组合导致了财务压力。根据估计,2017 年美国股票的 HFT 总收入首次跌破了 2008 年以来的 10 亿美元,从 2009 年的 79 亿美元下降。这一趋势导致了行业整合,例如,由最大的上市专营交易公司 Virtu Financial 进行的各种收购,以及共享基础设施投资,例如芝加哥到东京之间的新 Go West 超低延迟路线。与此同时,像 Alpha Trading Labs 这样的初创公司正在通过为分享利润的算法众包使 HFT 交易基础设施和数据变得可用,以使 HFT 民主化。

因子投资和智能贝塔基金

资产提供的回报是与投资相关的不确定性或风险的函数。例如,股票投资意味着承担公司的业务风险,而债券投资则意味着违约风险。在特定风险特征预测回报的程度上,识别和预测这些风险因素的行为成为设计投资策略时的主要关注点。它产生了有价值的交易信号,并且是实现优秀主动管理结果的关键。随着时间的推移,行业对风险因素的理解已经发生了非常大的变化,并且已经影响了 ML 用于交易的方式。第四章金融特征工程 - 如何研究 Alpha 因素,以及第五章投资组合优化和绩效评估,将更深入地探讨这里概念的实际应用;详见 Ang(2014)进行全面的覆盖。

现代投资组合理论MPT)引入了对给定资产的特异性和系统性风险来源的区分。特异性风险可以通过分散化来消除,但系统性风险则不能。20 世纪 60 年代初,资本资产定价模型CAPM)确定了推动所有资产回报的单一因素:市场投资组合超过国库券的回报。市场投资组合由所有可交易证券组成,按其市值加权。资产对市场的系统性暴露由贝塔来衡量,即资产回报与市场投资组合回报之间的相关性。

资产风险不仅仅取决于单个资产,而是取决于它与其他资产和整个市场的相对运动,这是一个重大的概念性突破。换句话说,资产根据它们暴露于所有资产共同面临的基础普遍风险,而不是由于它们特定的、特殊的特征,来获得风险溢价

随后,学术研究和行业经验提出了许多关于 CAPM 预测的关键问题,即资产的风险溢价仅取决于其暴露于由资产贝塔测量的单一因素。相反,已经发现了许多额外的风险因素。因素是一种可量化的信号、属性或任何变量,它在历史上与未来的股票回报相关,并且预计在未来仍然相关。

这些风险因素被标记为异常因素,因为它们与有效市场假说EMH)相矛盾。EMH 认为市场均衡总是根据 CAPM 定价证券,因此其他因素不应具有预测能力(Malkiel 2003)。因素背后的经济理论可以是理性的,即因素风险溢价补偿了在不景气时期的低回报,也可以是行为的,即代理人未能套利掉多余的回报。

知名的异常包括价值、规模和动量效应,它们有助于预测回报,同时控制 CAPM 市场因素。规模效应依赖于小型公司系统性地胜过大型公司(班兹,1981 年;Reinganum,1981 年)。价值效应(Basu 等,1981 年)表明具有低估价指标的公司胜过具有相反特征的同行。它表明,具有低价格倍数(例如市盈率或市净率)的公司比它们更昂贵的同行表现更好(正如价值投资的发明者本杰明·格雷厄姆和大卫·多德所建议,并由沃伦·巴菲特所推广)。

动量效应是在 1980 年代晚期被发现的,其中包括 Clifford Asness 等人,AQR 的创始合伙人,它表明,具有良好动量的股票,即近 6-12 个月回报表现良好的股票,未来的回报比市场风险相似的动量较差的股票高。研究人员还发现,价值和动量因素解释了美国以外股票的回报,以及其他资产类别,如债券、货币和大宗商品,以及其他风险因素(Jegadeesh 和 Titman,1993 年;Asness,Moskowitz 和 Pedersen,2013 年)。

在固定收益领域,价值策略被称为顺势买入收益率曲线,是一种期限溢价形式。在大宗商品领域,它被称为卷动回报,如果期货曲线呈上升趋势,则为正回报,否则为负回报。在外汇市场,价值策略被称为持有

还存在非流动性溢价。更不流动的证券以低价格交易,并且相对于更流动的同行,具有高平均超额回报。具有较高违约风险的债券的平均回报往往更高,反映了信用风险溢价。由于投资者愿意为避免回报暴跌而支付高波动性保险,因此在期权市场上出售波动性保护的卖方往往获得高回报。

多因素模型比市场组合更广泛、更多样地定义了风险。1976 年,史蒂芬·罗斯提出了套利定价理论,它主张投资者会因为无法分散的多个系统性风险而获得补偿(Roll 和 Ross,1984 年)。最重要的三个宏观因素是增长、通胀和波动性,除此之外还有生产力、人口统计和政治风险。1993 年,尤金·法玛和肯尼斯·弗伦奇将股票风险因素的规模和价值与市场因素结合成一个单一的三因子模型,更好地解释了横截面股票回报。他们后来添加了一个同时解释两个资产类别回报的模型,该模型还包括债券风险因素(法玛和弗伦奇,1993 年;2015 年)。

风险因素尤其吸引人的一点是它们的低或负相关性。例如,价值和动量风险因素呈负相关,降低了风险,并使风险调整后的回报超过了风险因素所暗示的收益。此外,利用杠杆和多空策略,因子策略可以组合成市场中性方法。在暴露于正面风险的证券中开设多头仓位,并在暴露于负面风险的证券中减仓或建立空头仓位的组合,使得动态风险溢价得以收集。

因此,解释超出 CAPM 的回报的因素被纳入了倾向于支持一个或多个因素的投资风格中,资产开始向基于因子的投资组合转移。2008 年金融危机突显了当投资者不关注基础因子风险时,资产类别标签可能极具误导性并造成虚假的分散化感,因为资产类别同时暴跌。

在过去几十年中,量化因子投资已经从基于两种或三种风格的简单方法演变为多因子智能或异类基准产品。智能基金在 2017 年突破了 1 万亿美元的资产管理规模,证明了这种混合投资策略的普及程度,该策略将主动和被动管理相结合。智能基金采取被动策略,但根据一个或多个因素进行修改,例如选择更便宜的股票或根据股息支付进行筛选,以产生更好的回报。这种增长与对传统主动管理者收取高额费用的批评以及对其业绩加强的审查趋势相一致。

在投资行业中,发现并成功预测风险因素的持续影响未来资产回报的过程,无论是单独还是与其他风险因素结合,都是机器学习激增的关键驱动因素,并将贯穿本书的主题。

算法先驱胜过人类

领先引入算法交易的公司的资产管理规模AUM)的业绩记录和增长,在激起投资者兴趣以及后续行业努力复制其成功方面发挥了关键作用。系统性基金与高频交易不同之处在于,交易可能持有时间更长,同时寻求利用套利机会,而不是仅仅追求速度优势。

大多或完全依赖算法决策的系统性策略最著名地由数学家詹姆斯·西蒙斯引入,他于 1982 年创立了文艺复兴技术,并将其打造成首屈一指的量化公司。其神秘的 Medallion 基金,对外关闭,自 1982 年以来年化回报率估计为 35%。

D. E. Shaw、Citadel 和 Two Sigma,这三家最著名的量化对冲基金,使用基于算法的系统化策略,在 2017 年首次成为总收益前 20 名的表现最好的基金之一,扣除费用,并自创立以来。

D. E. Shaw 公司成立于 1988 年,2019 年资产管理规模达到 500 亿美元,排名第三。由 Kenneth Griffin 于 1990 年创立的 Citadel 公司管理着 320 亿美元,排名第五。Two Sigma 公司仅在 2001 年由 D. E. Shaw 的前员工 John Overdeck 和 David Siegel 创立,从 2011 年的 80 亿美元资产管理规模增长到 2019 年的 600 亿美元。Bridgewater公司由雷·达里奥于 1975 年创立,2019 年资产管理规模超过 1600 亿美元,并且由于其 Pure Alpha 基金而继续领先,该基金还融合了系统化策略。

同样地,在《机构投资者》2018 年对冲基金 100 强榜单上,前四家最大的公司和前六家公司中的五家主要或完全依赖计算机和交易算法来做投资决策,并且它们在一个具有挑战性的环境中不断增加其资产。一些量化型公司上升了排名,并在某些情况下以两位数的百分比增加了其资产。排名第二的Applied Quantitative ResearchAQR)公司在 2017 年将其对冲基金资产增加了 48%,2018 年增加了 29%,达到了近 900 亿美元。

由机器学习驱动的基金吸引了 1 万亿美元的资产管理规模。

计算能力、数据可用性和统计方法的三次革命使得系统化、数据驱动策略的采用不仅更加引人注目和具有成本效益,而且是竞争优势的关键来源。

因此,算法方法不仅在率先采用这些策略的对冲基金行业中找到了更广泛的应用,而且还在更广泛的资产管理公司以及 ETF 等被动管理车辆中找到了应用。特别是,利用机器学习和算法自动化的预测分析在投资过程的各个环节中扮演着越来越重要的角色,包括从构思和研究到战略制定和组合构建、交易执行和风险管理等各个资产类别。

行业规模的估计各不相同,因为没有对定量或算法基金的客观定义。许多传统的对冲基金甚至包括共同基金和 ETF 都在引入基于计算机的策略或将其融入到人加机器的环境中的自主性方法中。

根据经济学人杂志,2016 年,系统性基金成为了美国股市机构交易的最大推动力量(忽略高频交易,高频交易主要充当中间商)。到 2019 年,它们占据了机构交易量的 35%以上,而 2010 年仅为 18%;仅有 10%的交易仍然是由传统的股票基金所致。以罗素 3000 指数衡量,美国股票的价值约为 31 万亿美元。三种由计算机管理的基金—指数基金、交易所交易基金(ETFs)和量化基金—约占了 35%,而传统对冲基金和其他互惠基金的人类管理者仅占 24%。

市场研究公司 Preqin 估计,几乎有 1500 家对冲基金的大部分交易都是依靠计算机模型的帮助。量化对冲基金现在负责投资者进行的所有美国股票交易的 27%,而 2013 年仅为 14%。但是很多使用数据科学家—或称之为量化交易员—他们反过来使用机器来建立大型统计模型。

然而,近年来,基金已经转向真正的机器学习,人工智能系统能够以速度分析大量数据,并通过这些分析改善自身。最近的例子包括 Rebellion Research、Sentient 和 Aidyia,它们依靠进化算法和深度学习来设计完全自动的人工智能AI)驱动的投资平台。

从核心对冲基金行业,算法策略的采用已经扩展到了互惠基金甚至被动管理的交易所交易基金,以智能贝塔基金的形式,以及自主基金的量化方法的形式。

量化基金的出现

主动投资管理已经演变出了两种不同的方法:系统性或量化)和自主投资。系统性方法依赖算法来识别跨许多证券的投资机会的可重复和数据驱动方法。相比之下,自主方法涉及对少量证券基本面的深入分析。随着基本面经理采取更多的数据科学驱动方法,这两种方法变得越来越相似。

即使基本面交易员现在也装备了定量技术,根据巴克莱的数据,占据了 55 亿美元的系统性资产。与特定公司无关,量化基金根据跨越广泛证券范围的模式和动态进行交易。根据巴克莱 2018 年编制的数据,这样的量化交易员占据了总对冲基金资产的约 17%

Point72,资产规模达到 140 亿美元,已经将大约一半的投资组合经理转向了人加机器的方法。Point72 还投资数千万美元到一个分析大量替代数据并将结果传递给交易员的团队中。

战略能力投资

三个趋势推动了数据在算法交易策略中的使用,并可能进一步将投资行业从裁量性转向定量风格:

  • 数字数据的可用性呈指数级增长

  • 计算能力和数据存储容量的成本降低导致了它们的增加

  • 用于分析复杂数据集的统计方法的进展

对相关能力——技术、数据以及最重要的是熟练人员——的投资不断增加,突显了利用机器学习进行算法交易对竞争优势的重要性,尤其是考虑到自 2008 年金融危机以来,被动指数投资工具(如 ETF)的流行度不断上升。

摩根士丹利指出,仅有 23%的量化客户表示他们不考虑使用或尚未使用机器学习,而 2016 年这一比例为 44%。古根海姆合伙公司在加利福尼亚州的劳伦斯伯克利国家实验室为 100 万美元建立了一个所谓的超级计算机集群,帮助为古根海姆的量化投资基金进行数据处理。电脑的电费每年还要花费 100 万美元。

AQR是一家依靠学术研究来识别并系统交易长期以来证明能够击败整体市场的因素的量化投资集团。该公司曾经回避了像文艺复兴技术或 DE Shaw 等量化同行纯计算机驱动的策略。然而,最近,AQR 已经开始使用机器学习在市场上寻找有利可图的模式,以分析新的数据集,例如油井和油船投射的卫星图片。

领先的公司黑石集团,管理着超过 5 万亿美元的资产,也通过大量投资于 SAE,一家在金融危机期间收购的系统性交易公司,来打败裁量性基金经理,押注算法。富兰克林·坦普尔顿公司以未披露的金额收购了 Random Forest Capital,一家以债务为重点、以数据为导向的投资公司,希望其技术能支持更广泛的资产管理。

机器学习和替代数据

信息优势和发现新的不相关信号的能力长期以来一直是对冲基金追求阿尔法的目标。历史上,这包括诸如对购物者的专有调查,或者对选举或公投前的选民的调查。

偶尔,利用公司内部人员、医生和专家网络来扩展对行业趋势或公司的了解,可能会越过法律界限:自 2010 年以来,一系列针对交易员、投资组合经理和分析师使用内部信息的起诉已经动摇了该行业。

相比之下,利用机器学习开发常规和替代数据源的信息优势不取决于专家和行业网络或者对公司管理层的接触,而是取决于收集大量非常多样的数据源并实时分析这些数据的能力。

传统数据包括经济统计数据、交易数据或公司报告。替代数据范围更广,包括卫星图像、信用卡销售、情感分析、移动地理位置数据和网站抓取等来源,以及将业务日常生成的数据转化为有价值情报。从原则上讲,它包括任何包含(潜在的)交易信号的数据来源

例如,来自保险公司关于新汽车保险政策销售的数据不仅捕捉到了新车销售的数量,还可以分解成品牌或地理区域。许多供应商从网站中抓取有价值的数据,范围从应用程序下载和用户评论到航空公司和酒店预订。社交媒体网站也可以被爬取以获取消费者观点和趋势的提示。

通常,这些数据集很大,需要使用可扩展的数据解决方案进行存储、访问和分析,例如 Hadoop 和 Spark。据德意志银行称,全球有超过 10 万亿个网页的 10 亿个网站,共有 500 艾字节(或 5000 亿吉字节)的数据。每年有超过 1 亿个网站被添加到互联网上。

在公司的业绩公布之前,可以通过其网站上职位招聘数量的下降、员工在招聘网站 Glassdoor 上对其首席执行官的内部评价或其网站上衣物平均价格的下降获得对公司前景的实时见解。这些信息可以与汽车停车场的卫星图像和移动电话的地理位置数据相结合,后者可以指示有多少人正在访问商店。另一方面,战略动向可以从特定功能区域或特定地理位置的职位发布量的增加中获得。

最有价值的信息来源之一是直接揭示消费者支出的数据,其中信用卡信息是主要来源。这些数据只提供了销售趋势的部分视图,但与其他数据结合使用时可以提供重要的见解。例如,Point72 曾经一度每天分析 8000 万笔信用卡交易。我们将在第三章金融替代数据-分类和用例中详细探讨各种数据来源、它们的用例以及如何评估它们。

投资集团在过去两年中将他们在替代数据和数据科学家上的支出翻了一番,因为资产管理行业试图振兴其日渐衰落的命运。在 2018 年 12 月,alternativedata.org(由 Yipit 提供支持)列出了 375 家替代数据提供商。

2017 年,资产管理公司总共花费了 3.73 亿美元用于数据集和雇佣新员工来解析这些数据,比 2016 年增长了 60%,今年可能会总共花费 6.16 亿美元,根据 alternativedata.org 投资者的调查。该网站预测,到 2020 年,总体支出将上升到 10 亿美元以上。一些估计甚至更高:咨询公司 Optimus 估计,投资者每年在替代数据上的支出约为 50 亿美元,并预计未来几年该行业将以每年 30% 的速度增长。

随着对宝贵数据源的竞争加剧,数据源合同中的排他性安排成为一个关键特征,以保持信息优势。与此同时,隐私问题日益严重,监管机构已经开始关注当前基本没有受到监管的数据提供商行业。

众包交易算法

近年来,一些算法交易公司开始提供投资平台,该平台提供数据访问和编程环境,以众包方式获取风险因素,这些因素成为投资策略或整个交易算法的一部分。主要例子包括 WorldQuant、Quantopian,以及最近的 Alpha Trading Labs(于 2018 年推出)。

WorldQuant 是 Millennium Management(资产管理规模:410 亿美元)于 2007 年拆分出来的子公司,为该公司管理大约 50 亿美元的资金。在其 alpha 工厂中,它雇佣了数百名科学家和更多的兼职工人,这个工厂将投资过程组织为一个定量流水线。该工厂声称已经生产了 400 万个成功测试的 alpha 因子,用于更复杂的交易策略,并且目标是 1 亿个。每个 alpha 因子都是一个算法,旨在预测未来的资产价格变化。然后其他团队将 alpha 因子组合成策略,策略组合成投资组合,在投资组合之间分配资金,并管理风险,同时避免相互损害的策略。请参见附录Alpha 因子库,了解 WorldQuant 使用的几十个定量因子的示例。

设计和执行基于机器学习的策略

在本书中,我们演示了机器学习如何融入设计执行评估交易策略的整个过程。为此,我们将假设基于机器学习的策略是由包含目标领域和策略的预测信号的数据源驱动的,这些信号经过适当的预处理和特征工程后,允许机器学习模型预测资产回报或其他策略输入。模型预测又会转化为基于人工裁量或自动规则的买入或卖出订单,这些规则又可以由另一个机器学习算法通过端到端的方法进行手动编码或学习。

图 1.1 描绘了这个工作流程的关键步骤,这也塑造了本书的组织结构:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1.1:ML4T 工作流程

第一部分介绍了适用于不同策略和 ML 用例的重要技能和技术。这些包括以下内容:

  • 如何获取和管理重要数据源

  • 如何设计具有信息量的特征或 alpha 因子以提取信号内容

  • 如何管理投资组合并跟踪策略绩效

此外,在第二部分的第八章ML4T 工作流程 - 从模型到策略回测中,涵盖了策略回测。在转向相关 ML 用例之前,我们将简要概述这些领域,这些用例构成本书第 2、3 和 4 部分的大部分内容。

数据的采集和管理

在数据可用性方面的体积,种类和速度的戏剧性演变是对 ML 应用于交易的重要补充,反过来又推动了行业对获取新数据源的支出。然而,数据供应的不断增加需要谨慎选择和管理,以发现潜在价值,包括以下步骤:

  1. 识别和评估不会迅速衰减的市场,基本和替代数据源中包含的 alpha 信号。

  2. 部署或访问基于云的可扩展数据基础设施和分析工具,如 Hadoop 或 Spark,以便快速,灵活地访问数据。

  3. 通过在特定时间点上将数据进行调整,精心管理和筛选数据以避免前瞻偏差。这意味着数据应该只反映在给定时间可用和已知的信息。在扭曲的历史数据上训练的机器学习算法几乎肯定会在实时交易中失败。

我们将在第二章市场和基本数据 - 来源和技术,以及第三章金融替代数据 - 类别和用例中详细介绍这些方面。

从 alpha 因子研究到投资组合管理

Alpha 因子旨在从数据中提取信号,以预测给定投资范围在交易期内的回报。典型因子在评估给定时间点时对每个资产都采用单个值,但可能组合一个或多个输入变量或时间段。如果您已经熟悉 ML 工作流程(参见第六章机器学习过程),您可以将 alpha 因子视为专为特定策略设计的领域特定特征。使用 alpha 因子涉及研究阶段和执行阶段,如图 1.2所述:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1.2:alpha 因子研究过程

研究阶段

研究阶段包括设计和评估 alpha 因子。预测因子捕获了数据源和重要策略输入(如资产回报)之间系统关系的某些方面。优化预测能力需要通过有效的数据转换进行创造性的特征工程。

由于数据挖掘导致的虚假发现是一个需要仔细管理的重要风险。降低这种风险的一种方式是通过遵循几十年的学术研究指导进行搜索,该研究产生了几个诺贝尔奖。许多投资者仍然更喜欢与金融市场和投资者行为理论相一致的因素。阐述这些理论超出了本书的范围,但参考资料突显了深入探讨这一重要框架的途径。

验证 alpha 因子的信号内容需要在代表性环境中得到其预测能力的健壮估计。有许多可能破坏可靠估计的方法论和实际陷阱。除了数据挖掘和未对多重检验偏差进行校正之外,这些陷阱还包括使用受到存活或前瞻性偏差污染的数据,以及不反映现实主、息、税PIT)信息。第四章金融特征工程 - 如何研究 Alpha 因子,讨论了如何成功地管理这个过程。

执行阶段

执行阶段,alpha 因子发出信号,导致买入或卖出订单。由此产生的投资组合持有,反过来又具有特定的风险配置,相互交互并对整体投资组合风险做出贡献。投资组合管理涉及优化仓位大小,以实现与投资目标一致的投资组合收益和风险的平衡。

第五章投资组合优化和绩效评估,介绍了适用于交易策略工作流程的这个阶段的关键技术和工具,从投资组合优化到绩效评估。

策略回测

将投资理念纳入实际算法策略中意味着需要科学方法来处理重大风险。这种方法包括通过广泛的经验性测试来拒绝基于其在备选样本市场情景中的表现而接受该理念。测试可能涉及使用模拟数据来捕捉可能发生但未反映在历史数据中的情景。

要为候选策略获得无偏的性能估计,我们需要一个回测引擎,以真实的方式模拟其执行。除了数据引入的潜在偏差或统计使用上的缺陷之外,回测引擎还需要准确地表现出交易信号评估、订单下达和执行方面的实际情况,符合市场条件。

第八章ML4T 工作流程 - 从模型到策略回测,展示了如何使用 backtrader 和 Zipline,并应对多种方法上的挑战,并完成了端到端 ML4T 工作流程的介绍。

用于交易的机器学习 - 策略和用例

实际上,我们将 ML 应用于特定策略的交易,以达到特定的业务目标。在本节中,我们简要描述了交易策略的演变和多样化,并概述了 ML 应用的实际例子,重点介绍了它们与本书内容的关系。

算法策略的演变

定量策略已经在三个阶段中发展并变得更加复杂:

  1. 在 20 世纪 80 年代和 90 年代,信号通常来源于学术研究,并使用从市场和基本数据派生的单一或极少量的输入。今天最大的定量对冲基金之一 AQR 于 1998 年成立,以大规模实施此类策略。这些信号现在主要是商品化的,并作为 ETF 提供,例如基本的均值回归策略。

  2. 在 2000 年代,基于因子的投资在 Eugene Fama 和 Kenneth French 等人的开创性工作基础上迅速蔓延。基金使用算法识别暴露于价值或动量等风险因素的资产,寻求套利机会。在金融危机早期的赎回触发了 2007 年 8 月的量化地震,这一地震经由基于因子的基金行业传播开来。这些策略现在也作为仅多头的智能 beta 基金提供,根据给定的风险因素倾斜投资组合。

  3. 第三个时代是通过投资于ML 能力和替代数据来生成可重复交易策略的盈利信号。因子衰减是一个主要挑战:新的异常收益从发现到发布后已经降低了四分之一,由于竞争和拥挤,发布后降低了超过 50%。

今天,当交易者使用算法执行规则时,他们追求一系列不同的目标:

  • 旨在实现有利定价的交易执行算法

  • 旨在从小的价格波动中获利的短期交易,例如由于套利而引起的

  • 旨在预测其他市场参与者行为的行为策略

  • 基于绝对价格和相对价格和回报预测的交易策略

交易执行程序旨在限制交易的市场影响,范围从简单的切片交易以匹配按时间加权或按成交量加权的平均定价。简单的算法利用历史模式,而更复杂的版本则考虑交易成本、执行差距或预测的价格波动。

高频交易基金最突出地依赖非常短的持有期,以从买卖价差或统计套利的微小价格波动中获益。行为算法通常在流动性较低的环境中运行,并旨在预测具有显著价格影响的较大玩家的动作,例如依据生成对其他市场参与者策略洞察的嗅探算法。

在本书中,我们将重点放在根据对不同时间范围内相对价格变化的预期进行交易的策略上,这些策略不受延迟优势的影响,因为它们被广泛使用且非常适合 ML 的应用。

用于交易的 ML 用例

ML 能够从各种市场、基本和替代数据中提取可交易的信号,因此适用于针对一系列资产类别和投资期限的策略。然而,更普遍地说,它是一个灵活的工具,用于支持或自动化具有可量化目标和数字数据相关的决策。因此,它可以应用于交易过程的几个步骤。在不同类别中有许多用例,包括:

  • 数据挖掘以识别模式、提取特征和生成洞察

  • 监督学习生成风险因素或 alpha 并创建交易思路

  • 将个别信号聚合成策略

  • 根据算法学习的风险配置资产

  • 策略的测试和评估,包括使用合成数据

  • 使用强化学习进行交互式自动化策略的优化

我们简要介绍了其中一些应用,并指出我们将在后面的章节中演示它们的用途。

数据挖掘以进行特征提取和洞察

大规模、复杂数据集的成本效益评估需要大规模检测信号。本书中有几个例子:

  • 信息论有助于估计候选特征的信号内容,因此有助于从 ML 模型中提取最有价值的输入。在第四章金融特征工程 - 如何研究 Alpha 因子中,我们使用互信息来比较个别特征对监督学习算法预测资产收益的潜在价值。De Prado(2018)的第十八章估计了价格序列的信息内容,作为在不同交易策略之间做出决策的基础。

  • 无监督学习提供了一系列方法来识别数据中的结构,以获得洞察或帮助解决下游任务。我们提供了几个例子:

    • 第十三章基于数据驱动的风险因素和无监督学习的资产配置中,我们介绍了聚类和降维以从高维数据集生成特征。

    • 第十五章主题建模 - 总结金融新闻中,我们应用贝叶斯概率模型对金融文本数据进行总结。

    • 第二十章自动编码器用于条件风险因素和资产定价中,我们使用深度学习提取根据资产特征条件化的非线性风险因素,并根据 Kelly 等人(2020)预测股票收益。

  • 模型透明度强调了获得对个别变量的预测能力的模型特定方法,并引入了一种新颖的博弈论方法,称为 SHapley 加法解释SHAP)。我们将其应用于具有大量输入变量的梯度提升机,见 第十二章提升您的交易策略,以及 附录Alpha 因子库

用于 alpha 因子创建的监督学习

将 ML 应用于交易的最熟悉理由之一是获得关于资产基本面、价格变动或市场状况的预测。一种策略可以利用多个建立在彼此之上的 ML 算法:

  • 下游模型可以通过整合关于个别资产前景、资本市场预期和证券之间的相关性的预测来在投资组合水平上生成信号。

  • 或者,ML 预测可以像之前概述的量化基本方法那样通知自由交易

ML 预测也可以针对特定的风险因素,例如价值或波动性,或者实施技术方法,例如趋势跟随或均值回归:

  • 第三章金融的另类数据 - 分类和用例 中,我们说明了如何处理基本数据,以创建 ML 驱动的估值模型的输入。

  • 第十四章用于交易的文本数据 - 情感分析第十五章主题建模 - 总结财经新闻,以及 第十六章用于盈利电话和 SEC 报告的词嵌入 中,我们使用了商业评论的另类数据,这些数据可以作为估值练习的输入,用于预测公司的收入。

  • 第九章用于波动率预测和统计套利的时间序列模型 中,我们演示了如何预测宏观变量作为市场预期的输入,以及如何预测诸如波动率之类的风险因素。

  • 第十九章多元时间序列和情感分析的 RNN 中,我们介绍了能够在非线性时间序列数据上取得优越性能的循环神经网络。

资产配置

ML 已经被用于基于决策树模型的资产配置,这些模型计算出一种分层形式的风险均衡。因此,风险特征受到资产价格模式的驱动,而不是资产类别,并且具有优越的风险-回报特性。

第五章投资组合优化和绩效评估,以及 第十三章无监督学习中的数据驱动风险因素和资产配置 中,我们说明了层次聚类如何提取数据驱动的风险类别,这些类别比传统的资产类别定义更好地反映了相关性模式(参见 De Prado 2018 中的 第十六章)。

测试交易理念

回测是选择成功的算法交易策略的关键步骤。使用合成数据进行交叉验证是一种关键的 ML 技术,当与适当的方法结合使用以校正多重测试时,可以生成可靠的样本外结果。财务数据的时间序列性质要求修改标准方法以避免前瞻偏差,否则会污染用于训练、验证和测试的数据。此外,历史数据的有限可用性催生了使用合成数据的替代方法。

我们将展示使用市场、基本和替代数据源测试 ML 模型的各种方法,以获得可靠的样本外错误估计。

第二十一章用于合成时间序列数据的生成对抗网络中,我们介绍了生成对抗网络GANs),它们能够生成高质量的合成数据。

强化学习

交易发生在竞争激烈的互动市场中。强化学习旨在训练代理学习基于奖励的策略函数;它通常被认为是金融 ML 中最有前景的领域之一。例如,参见 Hendricks 和 Wilcox(2014)以及 Nevmyvaka、Feng 和 Kearns(2006)的交易执行应用。

第二十二章深度强化学习 - 构建交易代理中,我们介绍了关键的强化学习算法,如 Q-learning,以演示使用 OpenAI 的 Gym 环境训练交易强化学习算法。

总结

在本章中,我们回顾了算法交易策略的关键行业趋势、替代数据的出现以及利用 ML 利用这些新信息优势的使用。此外,我们介绍了 ML4T 工作流程的关键要素,并概述了 ML 在不同策略背景下用于交易的重要用例。

在接下来的两章中,我们将更深入地研究推动任何算法交易策略的油料——市场、基本和替代数据来源——使用 ML。

第二章:市场和基本数据-来源和技术

数据一直是交易的重要驱动因素,交易员长期以来一直努力获得优势,以获取优越信息的访问权。这些努力至少可以追溯到有关罗斯柴尔德家族通过鸽子携带跨越海峡的关于滑铁卢战役英国胜利的消息事先从债券购买中受益丰厚的传闻。

如今,对更快数据访问的投资呈现为连接芝加哥商品交易所(CME)与东京的领先高频交易HFT)公司的 Go West 财团。CME 与纽约的BATSBetter Alternative Trading System)交易所之间的往返延迟已降至接近理论极限的八毫秒,因为交易者竞争利用套利机会。与此同时,监管机构和交易所已开始引入减速装置,以减缓交易速度,限制对信息不均匀访问的不利影响。

传统上,投资者主要依赖于公开市场和基本数据。通过专有调查等方式创建或获取私有数据集的努力有限。传统策略侧重于股票基本面,并在报告的财务数据上构建财务模型,可能结合行业或宏观数据来预测每股收益和股价。或者,他们利用技术分析从市场数据中提取信号,使用从价格和成交量信息计算出的指标。

机器学习ML)算法承诺比人定义的规则和启发式方法更有效地利用市场和基本数据,特别是当结合替代数据时,这是下一章的主题。我们将阐明如何应用从线性模型到递归神经网络RNNs)的 ML 算法到市场和基本数据,并生成可交易的信号。

本章介绍了市场和基本数据来源,并解释了它们反映的环境。交易环境的细节不仅对市场数据的正确解释至关重要,还对您的策略的设计和执行以及实施逼真的回测模拟至关重要。

我们还说明了如何使用 Python 访问和处理来自各种来源的交易和财务报表数据。

特别是,本章将涵盖以下主题:

  • 市场数据如何反映交易环境的结构

  • 在分钟频率下处理交易和报价数据

  • 从 tick 数据中重建订单簿使用纳斯达克 ITCH

  • 使用各种类型的条形图总结 tick 数据

  • 处理使用可扩展商业报告语言XBRL)编码的电子申报

  • 解析和组合市场和基本数据以创建市盈率P/E)序列

  • 如何使用 Python 访问各种市场和基本数据源

您可以在 GitHub 仓库的相应目录中找到本章的代码示例和额外资源的链接。笔记本包括图像的彩色版本。

市场数据反映了其环境

市场数据是交易者直接或通过中介在众多市场之一下订单的产品,以及订单是如何处理以及价格是如何通过匹配需求和供给来确定的。因此,数据反映了交易场所的制度环境,包括管理订单、交易执行和价格形成的规则和法规。详细全球概览请参阅 Harris (2003),美国市场的详细信息请参阅 Jones (2018)。

算法交易者使用算法,包括机器学习,来分析买卖订单流动以及由此产生的交易量和价格统计数据,以提取捕捉洞察力的交易信号,例如需求供给动态或特定市场参与者的行为。

在我们开始使用由纳斯达克这样的环境创建的实际刻度数据之前,我们将首先回顾在回测期间影响交易策略模拟的制度特征。

市场微观结构 – 细枝末节

市场微观结构研究了制度环境如何影响交易过程,以及形成价格发现、买卖报价和报价、日内交易行为以及交易成本等结果(Madhavan 2000; 2002)。它是金融研究中增长最快的领域之一,受到算法交易和电子交易的快速发展推动。

如今,对冲基金赞助内部分析师跟踪快速发展的、复杂的细节,并确保以最佳市场价格执行,并设计利用市场摩擦的策略。在我们深入研究交易生成的数据之前,我们将简要概述这些关键概念。参考文献中包含了许多详细介绍这一主题的来源。

如何交易 – 不同类型的订单

交易者可以下不同类型的买入或卖出订单。有些订单保证立即执行,而其他订单可能说明价格阈值或触发执行的其他条件。订单通常在同一交易日有效,除非另有说明。

市价订单旨在在到达交易场所时立即执行订单,以当时的价格执行。相比之下,限价订单仅在市场价格高于卖出限价订单的限价或低于买入限价订单的限价时执行。止损订单则仅在市场价格升至指定价格以上时对买入止损订单生效,或降至指定价格以下时对卖出订单生效。买入止损订单可用于限制空头交易的损失。止损订单也可能有限制条件。

订单可以附加许多其他条件。例如,全部或无订单防止部分执行;只有指定数量的股票可用时才会填充,并且可以有效期一天或更长时间。它们需要特殊处理,并且对市场参与者不可见。全部成交或取消订单也防止部分执行,但如果立即未执行则取消。立即成交或取消订单立即购买或出售可用数量的股票,并取消剩余部分。不指定时间的订单允许经纪人决定执行的时间和价格。最后,市场在 开盘/收盘 订单在市场开盘或收盘时执行或接近执行。部分执行是允许的。

何处交易 - 从交易所到深池

证券在高度组织化和受监管的交易所交易,或者在场外交易OTC)市场以不同程度的形式交易。交易所是买家和卖家竞争最低的要价和最高的出价的中央市场。交易所监管通常会实施上市和报告要求,以创建透明度并吸引更多的交易者和流动性。场外交易市场,例如最佳市场(OTCQX)或风险市场(OTCQB),通常具有较低的监管壁垒。因此,它们适用于更广泛范围的证券,包括债券或美国存托凭证ADRs;例如,在外国交易所上市的股票,例如雀巢公司)。

交易所可能依靠双边交易或根据特定规则匹配所有买入和卖出订单的集中式订单驱动系统。许多交易所使用提供流动性的中介,这些中介包括充当自己代理的交易商和代表他人交易的经纪人。价格形成可能通过拍卖进行,例如在纽约证券交易所NYSE)中,最高的出价和最低的要价被匹配,或者通过从卖方手中购买并向买方出售的交易商进行。

从前,公司要么在纽约证券交易所(NYSE)注册并进行交易,要么在场外交易市场如纳斯达克进行交易。在纽约证券交易所,一位专家负责中介特定证券的交易。专家通过经纪人接收买入和卖出订单,并在一个中央订单簿中跟踪限价订单。限价订单根据价格和时间优先执行。买入市价订单被路由到专家以与限价订单簿中最低的要价成交(卖出市价订单被路由到专家以与最高的出价成交),在平局的情况下优先考虑较早的限价订单。专家可以访问中央订单簿中的所有订单,从而公布最佳买价、卖价,并根据整体买卖失衡情况设置市场价格。

在纳斯达克,多个市场创造者促成了股票交易。每个经销商都向中央行情系统提供他们的最佳买入和卖出价格,并准备按指定价格以及指定数量进行交易。交易员通过他们的经纪人将订单路由到最佳报价的市场创造者处。订单的竞争很可能使执行价格公平。市场创造者确保了一个公平和有序的市场,提供了流动性,并像专家一样传播价格,但只能访问路由到他们的订单,而不是整个市场的供需。这种碎片化可能会导致难以确定公平价值市场价格的困难。

如今,交易已经分散;在美国,不再只有两个主要的交易场所,而是有十三个以上的展示交易场所,包括交易所和(未受监管的)另类交易系统ATSs),如电子通讯网络ECNs)。每个场所以不同的延迟报告交易到综合带,但规则不同,有多种不同的定价和排队模型。

下表列出了一些较大的全球交易所及截至 03/2018 12 个月的各类资产交易量,包括衍生品。通常,少数金融工具占据了大部分交易:

交易所股票
市值(亿美元)上市公司数量
纽约证券交易所23,138,626
纳斯达克 - 美国10,375,718
日本交易所集团公司6,287,739
上海证券交易所5,022,691
欧洲交易所4,649,073
香港交易及结算所4,443,082
伦敦证券交易所集团3,986,413
深圳证券交易所3,547,312
德国交易所2,339,092
印度孟买证券交易所有限公司2,298,179
印度国家证券交易所有限公司2,273,286
BATS 全球市场 - 美国
芝加哥期权交易所
国际证券交易所

前面提到的 ATS 包括数十个允许交易者匿名执行的暗池。据估计,2017 年,暗池交易占所有美国股票交易的 40%,而 2010 年估计为 16%。暗池出现在 20 世纪 80 年代,当时 SEC 允许经纪人匹配大宗股票的买方和卖方。高频电子交易的兴起和 2007 年 SEC 颁布的旨在通过透明度刺激竞争并削减交易成本的规则,作为全国市场体系监管Reg NMS)的一部分,推动了暗池的增长,因为交易者旨在避免大宗交易的可见性(Mamudi 2017)。Reg NMS 还建立了国家最佳买卖价NBBO)的要求,要求经纪人将订单路由到提供最佳价格的交易场所。

一些 ATS 被称为暗池,因为它们不广播预先交易的数据,包括传统交易所需要做的买卖订单的存在、价格和数量。然而,暗池在交易发生后向金融业监管局FINRA)报告有关交易的信息。因此,暗池在交易执行之后才开始对价格发现过程产生影响,但在第一章中列出的各种高频交易策略提供了保护。

在下一节中,我们将看到市场数据如何捕捉交易活动并反映美国市场的机构基础设施。

处理高频数据

两类市场数据涵盖了在美国交易所上市的数千家公司,这些公司在 Reg NMS 下交易:整合信息源将来自每个交易场所的交易和报价数据合并在一起,而每个独立的交易所则提供了专有产品,其中包含了该特定场所的额外活动信息。

在本节中,我们首先会展示纳斯达克提供的专有订单流数据,该数据代表了订单、交易和随之发生的价格的实际流动,按 tick 逐步进行。然后,我们将演示如何将这个连续的、不定时间到达的数据流规范化为固定时间间隔的条形图。最后,我们将介绍 AlgoSeek 的股票分钟条数据,其中包含了整合的交易和报价信息。在每种情况下,我们都将说明如何使用 Python 处理这些数据,以便您可以为您的交易策略利用这些来源。

如何处理纳斯达克订单簿数据

市场数据的主要来源是订单簿,它在整个交易日实时更新以反映所有交易活动。交易所通常以实时服务的形式提供这些数据,收费;然而,它们可能免费提供一些历史数据。

在美国,股票市场提供了三个层次的报价,即一级、二级和三级,这些报价提供了越来越细化的信息和功能:

  • 一级(L1):实时的买卖价信息,可从众多在线来源获取。

  • 二级(L2):提供特定市场做市商的买入和卖出价格信息,以及最近交易的规模和时间,以更好地了解给定股票的流动性。

  • 三级(L3):增加了输入或更改报价、执行订单和确认交易的能力,仅提供给市场做市商和交易所成员公司。访问三级报价允许注册经纪人满足最佳执行要求。

交易活动反映在由市场参与者发送的众多 订单消息 中。这些消息通常符合电子金融信息交换 (FIX) 通信协议,用于证券交易和市场数据的实时交换,或者符合本地交易所协议。

用 FIX 协议进行交易通信

就像 SWIFT 是后台(例如在交易结算中)消息传递的消息协议一样,FIX 协议是交易执行前和期间交易所、银行、经纪商、清算公司和其他市场参与者之间的 事实上的消息标准,富达投资和所罗门兄弟于 1992 年引入了 FIX,以便促进经纪商和机构客户之间的电子通信,而之前,他们是通过电话交换信息的。

它在全球股票市场中变得流行,然后扩展到外汇、固定收益和衍生品市场,进一步进入后期交易以支持一键处理。交易所提供 FIX 消息的访问作为实时数据源,由算法交易员进行解析,以跟踪市场活动,并且,例如,识别市场参与者的足迹并预测他们的下一步行动。

消息序列允许 重建订单簿。跨多个交易所的交易规模产生了大量(~10 TB)难以处理的非结构化数据,因此可能成为竞争优势的来源。

FIX 协议,目前版本为 5.0,是一个具有庞大关联行业专业人士社区的免费开放标准。它是自描述的,类似于较新的 XML,FIX 会话由底层的 传输控制协议 (TCP) 层支持。社区不断添加新功能。

该协议支持管道分隔的键值对,以及基于标签的 FIXML 语法。一个请求服务器登录的示例消息如下所示:

8=FIX.5.0|9=127|35=A|59=theBroker.123456|56=CSERVER|34=1|32=20180117- 08:03:04|57=TRADE|50=any_string|98=2|108=34|141=Y|553=12345|554=passw0rd!|10=131| 

Python 中有一些开源的 FIX 实现,可用于构建和解析 FIX 消息。服务提供商 Interactive Brokers 提供了一种基于 FIX 的 计算机对计算机接口 (CTCI) 用于自动化交易(参见 GitHub 仓库本章的资源部分)。

纳斯达克 TotalView-ITCH 数据源

虽然 FIX 占据主导市场份额,但交易所也提供原生协议。纳斯达克提供了一种名为 TotalView-ITCH 的直接数据反馈协议,允许订阅者从下单到执行或取消跟踪股票工具的个别订单。

此数据流的历史记录允许重建特定证券的订单簿,该订单簿跟踪活跃限价订单。订单簿通过列出每个价格点出价或要约的股票数量来揭示一天中的市场深度。它还可能确定特定买入和卖出订单的市场参与者,除非它们是匿名下单的。市场深度是流动性和大额市场订单的潜在价格影响的关键指标。

除了匹配市价和限价订单外,纳斯达克还进行拍卖或交叉交易,在市场开盘和收盘时执行大量交易。随着被动投资继续增长,交叉交易变得越来越重要,交易者寻找执行更大的股票交易的机会。TotalView 还传播纳斯达克开盘和收盘交叉以及纳斯达克 IPO/停牌交叉的净订单不平衡指示器NOII)。

如何解析二进制订单消息

ITCH v5.0 规范声明了与系统事件、股票特性、限价订单的下单和修改以及交易执行相关的 20 多种消息类型。它还包含了开盘和收盘交叉前的净订单不平衡信息。

纳斯达克提供了数个月的每日二进制文件样本。本章的 GitHub 存储库包含一个笔记本,parse_itch_order_flow_messages.ipynb,演示了如何下载和解析 ITCH 消息的样本文件。然后笔记本 rebuild_nasdaq_order_book.ipynb 继续重建给定股票的已执行交易和订单簿。

下表显示了 2019 年 10 月 30 日样本文件中最常见的消息类型的频率:

消息类型订单簿影响消息数量
A新的无属性限价订单127,214,649
D订单取消123,296,742
U订单取消并替换25,513,651
E全部或部分执行;原始订单可能有多条消息7,316,703
X部分取消后修改3,568,735
F添加属性订单1,423,908
P交易消息(非交叉)1,525,363
C在与初始显示价格不同的价格全部或部分执行129,729
Q交叉交易消息17,775

对于每条消息,规范详细说明了组件及其相应的长度和数据类型:

名称偏移量长度注释
消息类型01S系统事件消息。
股票定位12整数始终为 0。
跟踪号32整数纳斯达克内部跟踪号。
时间戳56整数从午夜开始的纳秒数。
订单参考号118整数在接收时分配给新订单的唯一参考号。
买/卖指示符191Alpha正在添加的订单类型:B = 买入订单,S = 卖出订单。
股票数量204整数与要添加到订单簿中的订单相关联的股票总数。
股票248Alpha股票代码,右侧用空格填充。
价格324价格 (4)新订单的显示价格。请参阅规范中的数据类型以获取字段处理注意事项。
归因364Alpha与输入订单相关联的纳斯达克市场参与者标识符。

Python 提供了 struct 模块,使用格式字符串解析二进制数据,该格式字符串通过指示 byte 字符串的各个组件的长度和类型来标识消息元素,如规范中所述。

让我们走过解析交易信息和重建订单簿所需的关键步骤:

  1. ITCH 解析器依赖于文件message_types.xlsx中提供的消息规范(有关详细信息,请参阅笔记本parse_itch_order_flow_messages.ipynb)。它根据formats字典组装格式字符串:

    formats = {
        ('integer', 2): 'H',  # int of length 2 => format string 'H'
        ('integer', 4): 'I',
        ('integer', 6): '6s', # int of length 6 => parse as string, 
          convert later
        ('integer', 8): 'Q',
        ('alpha', 1)  : 's',
        ('alpha', 2)  : '2s',
        ('alpha', 4)  : '4s',
        ('alpha', 8)  : '8s',
        ('price_4', 4): 'I',
        ('price_8', 8): 'Q',
    } 
    
  2. 解析器将消息规范转换为格式字符串和命名元组,其中包含了消息内容:

    # Get ITCH specs and create formatting (type, length) tuples
    specs = pd.read_csv('message_types.csv')
    specs['formats'] = specs[['value', 'length']].apply(tuple, 
                               axis=1).map(formats)
    # Formatting for alpha fields
    alpha_fields = specs[specs.value == 'alpha'].set_index('name')
    alpha_msgs = alpha_fields.groupby('message_type')
    alpha_formats = {k: v.to_dict() for k, v in alpha_msgs.formats}
    alpha_length = {k: v.add(5).to_dict() for k, v in alpha_msgs.length}
    # Generate message classes as named tuples and format strings
    message_fields, fstring = {}, {}
    for t, message in specs.groupby('message_type'):
        message_fields[t] = namedtuple(typename=t,
                                      field_names=message.name.tolist())
        fstring[t] = '>' + ''.join(message.formats.tolist()) 
    
  3. alpha 类型的字段需要后处理,如format_alpha函数中所定义:

    def format_alpha(mtype, data):
        """Process byte strings of type alpha"""
        for col in alpha_formats.get(mtype).keys():
            if mtype != 'R' and col == 'stock':
                data = data.drop(col, axis=1)
                continue
            data.loc[:, col] = (data.loc[:, col]
                                .str.decode("utf-8")
                                .str.strip())
            if encoding.get(col):
                data.loc[:, col] = data.loc[:, col].map(encoding.get(col))
        return data 
    

单日的二进制文件包含超过 3 亿条消息,总计超过 9 GB。该脚本将解析结果迭代附加到一个以 HDF5 格式存储的文件中,以避免内存限制。(有关 HDF5 格式的更多信息,请参阅本章后面的使用 pandas 进行高效数据存储部分。)

以下(简化的)代码处理二进制文件并生成按消息类型存储的解析订单:

with (data_path / file_name).open('rb') as data:
    while True:
        message_size = int.from_bytes(data.read(2), byteorder='big', 
                       signed=False)
        message_type = data.read(1).decode('ascii')
        message_type_counter.update([message_type])
        record = data.read(message_size - 1)
        message = message_fields[message_type]._make(
            unpack(fstring[message_type], record))
        messages[message_type].append(message)

        # deal with system events like market open/close
        if message_type == 'S':
            timestamp = int.from_bytes(message.timestamp, 
                                       byteorder='big')
            if message.event_code.decode('ascii') == 'C': # close
                store_messages(messages)
                break 

总结所有 8,500 只股票的交易活动

如预期的那样,这一天交易的 8,500 多种证券中只有少数占据了大部分交易:

with pd.HDFStore(itch_store) as store:
    stocks = store['R'].loc[:, ['stock_locate', 'stock']]
    trades = (store['P'].append(
            store['Q'].rename(columns={'cross_price': 'price'}),
            sort=False).merge(stocks))
trades['value'] = trades.shares.mul(trades.price)
trades['value_share'] = trades.value.div(trades.value.sum())
trade_summary = (trades.groupby('stock').value_share
                 .sum().sort_values(ascending=False))
trade_summary.iloc[:50].plot.bar(figsize=(14, 6),
                                 color='darkblue',
                                 title='Share of Traded Value')
f = lambda y, _: '{:.0%}'.format(y)
plt.gca().yaxis.set_major_formatter(FuncFormatter(f)) 

图 2.1 显示了生成的图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.1:50 只最活跃证券的交易价值份额

如何重建所有交易和订单簿

解析后的消息允许我们重建给定日期的订单流。'R'消息类型包含了给定日期内所有交易的清单,包括首次公开发行IPO)和交易限制的信息。

在一天中,新订单被添加,而执行和取消的订单则从订单簿中移除。对于引用前日期放置的订单的消息的正确核算将需要跟踪多天的订单簿。

get_messages()函数说明了如何收集影响交易的单只股票的订单(有关每条消息的详细信息,请参阅 ITCH 规范)。代码略有简化;有关更多详细信息,请参阅笔记本rebuild_nasdaq_order_book.ipynb

def get_messages(date, stock=stock):
    """Collect trading messages for given stock"""
    with pd.HDFStore(itch_store) as store:
        stock_locate = store.select('R', where='stock = 
                                     stock').stock_locate.iloc[0]
        target = 'stock_locate = stock_locate'
        data = {}
        # relevant message types
        messages = ['A', 'F', 'E', 'C', 'X', 'D', 'U', 'P', 'Q']
        for m in messages:
            data[m] = store.select(m,  
              where=target).drop('stock_locate', axis=1).assign(type=m)
    order_cols = ['order_reference_number', 'buy_sell_indicator', 
                  'shares', 'price']
    orders = pd.concat([data['A'], data['F']], sort=False,  
                        ignore_index=True).loc[:, order_cols]
    for m in messages[2: -3]:
        data[m] = data[m].merge(orders, how='left')
    data['U'] = data['U'].merge(orders, how='left',
                                right_on='order_reference_number',
                                left_on='original_order_reference_number',
                                suffixes=['', '_replaced'])
    data['Q'].rename(columns={'cross_price': 'price'}, inplace=True)
    data['X']['shares'] = data['X']['cancelled_shares']
    data['X'] = data['X'].dropna(subset=['price'])
    data = pd.concat([data[m] for m in messages], ignore_index=True, 
                      sort=False) 

重建成功交易 - 即已执行而非取消的订单 - 与与交易相关的消息类型CEPQ相对简单。

def get_trades(m):
    """Combine C, E, P and Q messages into trading records"""
    trade_dict = {'executed_shares': 'shares', 'execution_price': 'price'}
    cols = ['timestamp', 'executed_shares']
    trades = pd.concat([m.loc[m.type == 'E',
                              cols + ['price']].rename(columns=trade_dict),
                        m.loc[m.type == 'C',
                              cols + ['execution_price']]
                        .rename(columns=trade_dict),
                        m.loc[m.type == 'P', ['timestamp', 'price',
                                              'shares']],
                        m.loc[m.type == 'Q',
                              ['timestamp', 'price', 'shares']]
                        .assign(cross=1), ],
                       sort=False).dropna(subset=['price']).fillna(0)
    return trades.set_index('timestamp').sort_index().astype(int) 

订单簿跟踪限价订单,并且买单和卖单的各种价格水平构成了订单簿的深度。重建给定深度级别的订单簿需要以下步骤:

add_orders()函数按升序累积卖单,按降序累积买单,直到达到所需的深度级别的给定时间戳:

def add_orders(orders, buysell, nlevels):
    new_order = []
    items = sorted(orders.copy().items())
    if buysell == 1:
        items = reversed(items)  
    for i, (p, s) in enumerate(items, 1):
        new_order.append((p, s))
        if i == nlevels:
            break
    return orders, new_order 

我们遍历所有 ITCH 消息,并根据规范处理订单及其替换所需的订单:

for message in messages.itertuples():
    i = message[0]
    if np.isnan(message.buy_sell_indicator):
        continue
    message_counter.update(message.type)
    buysell = message.buy_sell_indicator
    price, shares = None, None
    if message.type in ['A', 'F', 'U']:
        price, shares = int(message.price), int(message.shares)
        current_orders[buysell].update({price: shares})
        current_orders[buysell], new_order = 
          add_orders(current_orders[buysell], buysell, nlevels)
        order_book[buysell][message.timestamp] = new_order
    if message.type in ['E', 'C', 'X', 'D', 'U']:
        if message.type == 'U':
            if not np.isnan(message.shares_replaced):
                price = int(message.price_replaced)
                shares = -int(message.shares_replaced)
        else:
            if not np.isnan(message.price):
                price = int(message.price)
                shares = -int(message.shares)
        if price is not None:
            current_orders[buysell].update({price: shares})
            if current_orders[buysell][price] <= 0:
                current_orders[buysell].pop(price)
            current_orders[buysell], new_order = 
              add_orders(current_orders[buysell], buysell, nlevels)
            order_book[buysell][message.timestamp] = new_order 

图 2.2突显了任意给定时间点的流动性深度,使用不同的强度来可视化不同价格水平上的订单数量。左侧面板显示了限价订单价格的分布是如何向更高价格的买单倾斜的。

右侧面板绘制了整个交易日限价订单和价格的演变情况:深色线跟踪了市场小时内执行交易的价格,而红色和蓝色点表示每分钟基础上的单个限价订单(有关详细信息,请参阅笔记本):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.2:根据订单簿的 AAPL 市场流动性

从刻度到条形图 - 如何规范市场数据

交易数据按纳秒索引,以不规则间隔到达,且非常嘈杂。例如,买卖盘弹跳会导致价格在买入和卖出价格之间震荡,当交易启动在买入和卖出市价订单之间交替时。为了改善噪声-信号比和价格序列的统计特性,我们需要通过聚合交易活动对刻度数据进行重新采样和规范化。

我们通常收集聚合周期内的开盘价(第一)、最高价、最低价和收盘价(最后)以及成交量(联合简称为OHLCV),以及与数据相关的成交量加权平均价格VWAP)和时间戳。

有关附加详细信息,请参阅 GitHub 上本章节文件夹中的normalize_tick_data.ipynb笔记本。

原材料 - 刻度条

以下代码生成了 AAPL 的原始刻度价格和成交量数据的图表:

stock, date = 'AAPL', '20191030'
title = '{} | {}'.format(stock, pd.to_datetime(date).date()
with pd.HDFStore(itch_store) as store:
    sys_events = store['S'].set_index('event_code') # system events
    sys_events.timestamp = sys_events.timestamp.add(pd.to_datetime(date)).dt.time
    market_open = sys_events.loc['Q', 'timestamp'] 
    market_close = sys_events.loc['M', 'timestamp']
with pd.HDFStore(stock_store) as store:
    trades = store['{}/trades'.format(stock)].reset_index()
trades = trades[trades.cross == 0] # excluding data from open/close crossings
trades.price = trades.price.mul(1e-4) # format price
trades = trades[trades.cross == 0]    # exclude crossing trades
trades = trades.between_time(market_open, market_close) # market hours only
tick_bars = trades.set_index('timestamp')
tick_bars.index = tick_bars.index.time
tick_bars.price.plot(figsize=(10, 5), title=title), lw=1) 

图 2.3显示了生成的图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.3:刻度条

刻度回报远非正态分布,这表明scipy.stats.normaltest的 p 值很低:

from scipy.stats import normaltest
normaltest(tick_bars.price.pct_change().dropna())
NormaltestResult(statistic=62408.76562431228, pvalue=0.0) 

常规去噪 - 时间条

时间条涉及按周期进行交易聚合。以下代码获取时间条的数据:

def get_bar_stats(agg_trades):
    vwap = agg_trades.apply(lambda x: np.average(x.price, 
           weights=x.shares)).to_frame('vwap')
    ohlc = agg_trades.price.ohlc()
    vol = agg_trades.shares.sum().to_frame('vol')
    txn = agg_trades.shares.size().to_frame('txn')
    return pd.concat([ohlc, vwap, vol, txn], axis=1)
resampled = trades.groupby(pd.Grouper(freq='1Min'))
time_bars = get_bar_stats(resampled) 

我们可以将结果显示为价格-成交量图表:

def price_volume(df, price='vwap', vol='vol', suptitle=title, fname=None):
    fig, axes = plt.subplots(nrows=2, sharex=True, figsize=(15, 8))
    axes[0].plot(df.index, df[price])
    axes[1].bar(df.index, df[vol], width=1 / (len(df.index)), 
                color='r')
    xfmt = mpl.dates.DateFormatter('%H:%M')
    axes[1].xaxis.set_major_locator(mpl.dates.HourLocator(interval=3))
    axes[1].xaxis.set_major_formatter(xfmt)
    axes[1].get_xaxis().set_tick_params(which='major', pad=25)
    axes[0].set_title('Price', fontsize=14)
    axes[1].set_title('Volume', fontsize=14)
    fig.autofmt_xdate()
    fig.suptitle(suptitle)
    fig.tight_layout()
    plt.subplots_adjust(top=0.9)
price_volume(time_bars) 

上述代码产生了图 2.4

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.4:时间柱

或者,我们可以使用 Bokeh 绘图库将数据表示为蜡烛图:

resampled = trades.groupby(pd.Grouper(freq='5Min')) # 5 Min bars for better print
df = get_bar_stats(resampled)
increase = df.close > df.open
decrease = df.open > df.close
w = 2.5 * 60 * 1000 # 2.5 min in ms
WIDGETS = "pan, wheel_zoom, box_zoom, reset, save"
p = figure(x_axis_type='datetime', tools=WIDGETS, plot_width=1500, 
          title = "AAPL Candlestick")
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha=0.4
p.segment(df.index, df.high, df.index, df.low, color="black")
p.vbar(df.index[increase], w, df.open[increase], df.close[increase], 
       fill_color="#D5E1DD", line_color="black")
p.vbar(df.index[decrease], w, df.open[decrease], df.close[decrease], 
       fill_color="#F2583E", line_color="black")
show(p) 

这会产生图 2.5中的图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.5:Bokeh 蜡烛图

考虑订单碎片化 – 交易量柱

时间柱平滑了原始 tick 数据中包含的一些噪音,但可能未能解决订单碎片化的问题。执行重点的算法交易可能会在给定期间内匹配成交量加权平均价格 (VWAP)。这将将单个订单拆分为多个交易,并根据历史模式下订单。时间柱将以不同的方式处理相同的订单,即使市场中没有新的信息到来。

交易量柱提供了一种通过按交易量聚合交易数据的替代方法。我们可以按如下方式完成:

min_per_trading_day = 60 * 7.5
trades_per_min = trades.shares.sum() / min_per_trading_day
trades['cumul_vol'] = trades.shares.cumsum()
df = trades.reset_index()
by_vol = (df.groupby(df.cumul_vol.
                     div(trades_per_min)
                     .round().astype(int)))
vol_bars = pd.concat([by_vol.timestamp.last().to_frame('timestamp'),
                      get_bar_stats(by_vol)], axis=1)
price_volume(vol_bars.set_index('timestamp')) 

对于上述代码,我们得到了图 2.6的图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.6:交易量柱

考虑价格变化 – 美元柱

当资产价格发生显着变化或股票分拆后,给定数量的股票价值会发生变化。交易量柱无法正确反映这一点,并且可能会妨碍对反映此类变化的不同期间的交易行为进行比较。在这些情况下,应调整交易量柱方法以利用股票和价格的乘积产生美元柱。

下面的代码显示了美元柱的计算:

value_per_min = trades.shares.mul(trades.price).sum()/(60*7.5) # min per trading day
trades['cumul_val'] = trades.shares.mul(trades.price).cumsum()
df = trades.reset_index()
by_value = df.groupby(df.cumul_val.div(value_per_min).round().astype(int))
dollar_bars = pd.concat([by_value.timestamp.last().to_frame('timestamp'), get_bar_stats(by_value)], axis=1)
price_volume(dollar_bars.set_index('timestamp'), 
             suptitle=f'Dollar Bars | {stock} | {pd.to_datetime(date).date()}') 

由于价格在整个交易日都相当稳定,该图与交易量柱非常相似:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.7:美元柱

AlgoSeek 分钟柱 – 股票报价和交易数据

AlgoSeek 提供了以前仅供机构投资者使用的历史日内交易数据。AlgoSeek 股票柱以用户友好的格式提供了非常详细的日内报价和交易数据,旨在使设计和回测日内 ML 驱动策略变得容易。正如我们将看到的,该数据不仅包括 OHLCV 信息,还包括盘口报价价差和上下价格变动的次数等其他信息。

AlgoSeek 提供了 2013-2017 年纳斯达克 100 只股票分钟柱数据样本,以供演示,并将提供此数据的子集给本书读者。

在本节中,我们将介绍可用的交易和报价信息,并展示如何处理原始数据。在后面的章节中,我们将演示如何使用这些数据进行 ML 驱动的日内策略。

从综合信息流到分钟柱

AlgoSeek 分钟柱基于由 证券信息处理器 (SIP) 提供的数据,该处理器管理本节开头提到的综合信息流。您可以在 www.algoseek.com/samples/ 上查看文档。

SIP(股票信息处理器)汇总了来自每个交易所的最佳买价和卖价报价,以及相应的交易和价格。根据法律规定,交易所在将报价和交易发送到直接数据源之前,不得将其发送到 SIP。鉴于美国股票交易的碎片化特性,综合数据源提供了市场当前状态的便捷快照。

更重要的是,SIP 作为监管机构根据 Reg NMS 确定全国最佳买卖报价NBBO)的基准。OHLC K 线报价价格基于 NBBO,每个买入或卖出报价价格都指的是 NBBO 价格。

每个交易所都会发布其最优报价和该价格可获得的股票数量。当已发布的报价改善 NBBO 时,NBBO 就会变化。买入/卖出报价会持续存在,直到由于交易、价格改进或取消最新的买入或卖出而发生变化。虽然历史 OHLC(开盘、最高、最低、收盘)K 线通常基于该时间段内的交易,但 NBBO 买卖报价可能会从前一个时间段一直延续到发生新的 NBBO 事件。

AlgoSeek K 线覆盖了整个交易日,从第一个交易所开盘到最后一个交易所收盘。在正常交易时间之外的 K 线通常表现出有限的活动。交易时间(东部时间)如下:

  • 盘前市场:大约从 04:00:00(根据交易所而异)到 09:29:59

  • 市场交易时间:09:30:00 到 16:00:00

  • 延长交易时间:16:00:01 到 20:00:00

报价和交易数据字段

分钟级 K 线数据包含多达 54 个字段。对于 K 线的开盘价最高价最低价收盘价元素,有八个字段,即:

  • K 线的时间戳及相应的交易

  • 主要买卖报价和相关交易的价格和数量

还有 14 个带有成交量信息的数据点:

  • 股票数量及相应的交易

  • 在买入价以下、买入价和中间价之间、中间价、中间价和卖出价之间以及在卖出价以上的交易量,以及交叉交易量

  • 与前一个价格运动方向不同的价格上涨或下跌的交易量,以及价格未发生变化时的交易量

AlgoSeek 数据还包含报告给 FINRA 并由经纪商、暗池或场外交易所内部处理的股票数量。这些交易代表着隐藏或直到事后才可公开获取的成交量。

最后,数据包括成交量加权平均价格VWAP)和该时间段的最小和最大买卖价差。

如何处理 AlgoSeek 的日内数据

在本节中,我们将处理 AlgoSeek 的示例数据。GitHub 上的 data 目录包含了如何从 AlgoSeek 下载数据的说明。

分钟级数据有四个版本:带有和不带有报价信息,以及带有或不带有 FINRA 报告的成交量。每天有一个压缩文件夹,其中包含一个 CSV 文件。

下面的代码示例将交易分钟级数据提取到每日的 .parquet 文件中:

directories = [Path(d) for d in ['1min_trades']]
target = directory / 'parquet'
for zipped_file in directory.glob('*/**/*.zip'):
    fname = zipped_file.stem
    print('\t', fname)
    zf = ZipFile(zipped_file)
    files = zf.namelist()
    data = (pd.concat([pd.read_csv(zf.open(f),
                                   parse_dates=[['Date',
                                                 'TimeBarStart']])
                       for f in files],
                      ignore_index=True)
            .rename(columns=lambda x: x.lower())
            .rename(columns={'date_timebarstart': 'date_time'})
            .set_index(['ticker', 'date_time']))
    data.to_parquet(target / (fname + '.parquet')) 

我们可以将 parquet 文件合并为单个 HDF5 存储,产生 5380 万条记录,占用 3.2 GB 的内存,涵盖了 5 年和 100 支股票:

path = Path('1min_trades/parquet')
df = pd.concat([pd.read_parquet(f) for f in path.glob('*.parquet')]).dropna(how='all', axis=1)
df.columns = ['open', 'high', 'low', 'close', 'trades', 'volume', 'vwap']
df.to_hdf('data.h5', '1min_trades')
print(df.info(null_counts=True))
MultiIndex: 53864194 entries, (AAL, 2014-12-22 07:05:00) to (YHOO, 2017-06-16 19:59:00)
Data columns (total 7 columns):
open      53864194 non-null float64
high      53864194 non-null float64
Low       53864194 non-null float64
close     53864194 non-null float64
trades    53864194 non-null int64
volume    53864194 non-null int64
vwap      53852029 non-null float64 

我们可以使用 plotly 快速创建一个交互式蜡烛图,以查看 AAPL 数据的一天在浏览器中的显示:

idx = pd.IndexSlice
with pd.HDFStore('data.h5') as store:
    print(store.info())
    df = (store['1min_trades']
          .loc[idx['AAPL', '2017-12-29'], :]
          .reset_index())
fig = go.Figure(data=go.Ohlc(x=df.date_time,
                             open=df.open,
                             high=df.high,
                             low=df.low,
                             close=df.close)) 

图 2.8 显示了生成的静态图像:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.8:Plotly 蜡烛图

AlgoSeek 还提供调整因子,以纠正股票拆分、股息和其他公司行动的定价和成交量。

API 访问市场数据

有几种选项可供您使用 Python 通过 API 访问市场数据。我们首先介绍了内置于 pandas 库和yfinance工具中的一些来源,该工具便于从 Yahoo! Finance 下载每日市场数据和最近的基本数据。

然后,我们将简要介绍交易平台 Quantopian、数据提供者 Quandl 以及本书后续将使用的 Zipline 回测库,以及列出访问各种类型市场数据的几个其他选项。GitHub 上的 data_providers 目录包含了几个笔记本,说明了这些选项的使用情况。

使用 pandas 远程数据访问

pandas 库通过相关的 pandas-datareader 库实现通过 read_html 函数访问网站上显示的数据以及访问各种数据提供者的 API 端点。

读取 HTML 表格

下载一个或多个 HTML 表格的内容(例如来自 Wikipedia 的标准普尔 500 指数成分)的过程如下:

sp_url = 'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies'
sp = pd.read_html(sp_url, header=0)[0] # returns a list for each table
sp.info()
RangeIndex: 505 entries, 0 to 504
Data columns (total 9 columns):
Symbol                    505 non-null object
Security                  505 non-null object
SEC filings                505 non-null object
GICS Sector               505 non-null object
GICS Sub Industry         505 non-null object
Headquarters Location     505 non-null object
Date first added           408 non-null object
CIK                       505 non-null int64
Founded                   234 non-null object 

用于市场数据的 pandas-datareader

pandas 用于直接访问数据提供者的 API,但此功能已移至 pandas-datareader 库(请参阅 README 获取文档链接)。

API 的稳定性因提供者政策而异,且不断变化。请参阅文档以获取最新信息。截至 2019 年 12 月,版本 0.8.1,以下来源可用:

Source范围评论
Tiingo提供股票、共同基金和 ETF 的历史收盘价。免费注册获取 API 密钥。免费账户只能访问 500 个符号。
Investor Exchange (IEX)如果在 IEX 上交易,则提供历史股价。需要从 IEX Cloud 控制台获取 API 密钥。
Alpha Vantage每日、每周和每月频率的历史股票数据,20+ 年,以及过去 3-5 天的分钟级数据。它还具有外汇和行业绩效数据。
Quandl免费数据来源如它们网站上所列。
Fama/French风险因子投资组合回报。用于 第七章线性模型 - 从风险因素到回报预测
TSP 基金数据共同基金价格。
纳斯达克交易标的的最新元数据。
Stooq 指数数据由于许可问题,某些股票指数在其他地方不可用。
MOEX莫斯科交易所历史数据。

数据的访问和检索遵循所有来源的类似 API,如雅虎财经所示:

import pandas_datareader.data as web
from datetime import datetime
start = '2014'              # accepts strings
end = datetime(2017, 5, 24) # or datetime objects
yahoo= web.DataReader('FB', 'yahoo', start=start, end=end)
yahoo.info()
DatetimeIndex: 856 entries, 2014-01-02 to 2017-05-25
Data columns (total 6 columns):
High         856 non-null float64
Low          856 non-null float64
Open         856 non-null float64
Close        856 non-null float64
Volume       856 non-null int64
Adj Close    856 non-null float64
dtypes: float64(5), int64(1) 

yfinance – 从雅虎财经获取数据

yfinance 旨在提供从雅虎财经下载历史市场数据的可靠快速方法。该库最初命名为 fix-yahoo-finance。使用该库非常简单;笔记本 yfinance_demo 展示了该库的功能。

如何下载每日和分钟价格

Ticker 对象允许从雅虎网站抓取的各种数据点的下载:

import yfinance as yf
symbol = 'MSFT'
ticker = yf.Ticker(symbol) 

.history 方法获取各种期间的历史价格,从一天到可用的最大值,并以不同频率,而分钟内仅适用于最近几天。要下载调整后的 OHLCV 数据以及公司行动,请使用:

data = ticker.history(period='5d',
                      interval='1m',
                      actions=True,
                      auto_adjust=True)
data.info()
DatetimeIndex: 1747 entries, 2019-11-22 09:30:00-05:00 to 2019-11-29 13:00:00-05:00
Data columns (total 7 columns):
Open            1747 non-null float64
High            1747 non-null float64
Low             1747 non-null float64
Close           1747 non-null float64
Volume          1747 non-null int64
Dividends       1747 non-null int64
Stock Splits    1747 non-null int64 

该笔记本还说明了如何访问季度和年度财务报表、可持续性评分、分析师建议和即将公布的收益日期。

如何下载期权链和价格

yfinance 还提供了访问期权到期日期和价格以及其他各种合约信息的功能。使用前面示例中的 ticker 实例,我们可以使用以下方式获取到期日期:

ticker.options
('2019-12-05',  '2019-12-12',  '2019-12-19',..) 

对于任何这些日期,我们可以访问期权链,并查看各种看涨/看跌合约的详情,如下所示:

options = ticker.option_chain('2019-12-05')
options.calls.info()
Data columns (total 14 columns):
contractSymbol       35 non-null object
lastTradeDate        35 non-null datetime64[ns]
strike               35 non-null float64
lastPrice            35 non-null float64
bid                  35 non-null float64
ask                  35 non-null float64
change               35 non-null float64
percentChange        35 non-null float64
volume               34 non-null float64
openInterest         35 non-null int64
impliedVolatility    35 non-null float64
inTheMoney           35 non-null bool
contractSize         35 non-null object
currency             35 non-null object 

该库还允许使用代理服务器以防止速率限制,并便于批量下载多个股票。笔记本演示了这些功能的使用。

Quantopian

Quantopian 是一家提供研究平台以集体创建交易算法的投资公司。注册是免费的,会员可以使用各种数据源研究交易想法。它还提供了一个环境,可以使用历史数据对算法进行回测,以及使用实时数据对其进行样本外的前向测试。它为表现优秀的算法授予投资分配,其作者有权获得 10%(写作时)的利润份额。

Quantopian 研究平台由用于 alpha 因子研究和绩效分析的 Jupyter Notebook 环境组成。还有一个用于编写算法策略和使用从 2002 年以来带有分钟频率的历史数据回测结果的 交互式开发环境 (IDE)。

用户还可以使用实时数据模拟算法,这被称为模拟交易。Quantopian 提供各种市场数据集,包括美国股票和期货价格和交易量数据,以及美国股票公司基本面数据,还整合了众多备选数据集。

我们将在第四章金融特征工程——如何研究 Alpha 因子中更详细地探讨 Quantopian 平台,并在整本书中依赖其功能,所以请随时开设一个账户。(有关更多详细信息,请参阅 GitHub 存储库。)

Zipline

Zipline 是算法交易库,为 Quantopian 回测和实时交易平台提供支持。它也可以离线使用,用于开发策略,使用有限数量的免费数据包进行测试,然后将结果传输到在线 Quantopian 平台进行模拟和实时交易。

Zipline 需要一个定制环境——查看笔记本zipline_data_demo.ipynb开头的说明。以下代码说明了 Zipline 如何允许我们访问一系列公司的每日股票数据。您可以在 Jupyter Notebook 中使用同名的魔术函数运行 Zipline 脚本。

首先,您需要使用所需的安全符号初始化上下文。我们还将使用一个计数器变量。然后,Zipline 调用handle_data,我们使用data.history()方法回溯一个时间段,并将上一天的数据附加到一个.csv文件中:

%load_ext zipline
%%zipline --start 2010-1-1 --end 2018-1-1 --data-frequency daily
from zipline.api import order_target, record, symbol
def initialize(context):
    context.i = 0
    context.assets = [symbol('FB'), symbol('GOOG'), symbol('AMZN')]

def handle_data(context, data):
    df = data.history(context.assets, fields=['price', 'volume'], 
                      bar_count=1, frequency="1d")
    df = df.to_frame().reset_index()

    if context.i == 0:
        df.columns = ['date', 'asset', 'price', 'volume']
        df.to_csv('stock_data.csv', index=False)
    else:
        df.to_csv('stock_data.csv', index=False, mode='a', header=None)
    context.i += 1
df = pd.read_csv('stock_data.csv')
df.date = pd.to_datetime(df.date)
df.set_index('date').groupby('asset').price.plot(lw=2, legend=True, 
       figsize=(14, 6)); 

我们得到了前述代码的以下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.9:Zipline 数据访问

我们将在接下来的章节中更详细地探讨 Zipline 的功能,特别是在线 Quantopian 平台。

Quandl

Quandl 提供广泛的数据源,包括免费数据和订阅数据,使用 Python API。注册并获取免费 API 密钥,可每天进行 50 多次调用。Quandl 数据涵盖除股票以外的多种资产类别,包括外汇、固定收益、指数、期货和期权以及大宗商品。

API 使用简单,文档完善,灵活,除了单个系列下载外,还有许多其他方法,例如,包括批量下载或元数据搜索。

下面的调用从 1986 年开始获取美国能源部的报价的油价:

import quandl
oil = quandl.get('EIA/PET_RWTC_D').squeeze()
oil.plot(lw=2, title='WTI Crude Oil Price') 

我们从前面的代码中得到了这个图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.10:Quandl 油价示例

其他市场数据提供商

各种提供商提供各种资产类别的市场数据。相关类别的示例包括:

  • 交易所越来越多地从越来越广泛的数据服务中获得收入,通常使用订阅方式。

  • 彭博社和汤姆森路透一直是领先的数据聚合商,在 285 亿美元的金融数据市场中占据了超过 55%的份额。较小的竞争对手,如 FactSet,正在增长或出现,如 money.net,Quandl,Trading Economics 和 Barchart。

  • 专业数据提供商比比皆是。一个例子是 LOBSTER,它实时聚合纳斯达克订单簿数据。

  • 免费数据提供商包括 Alpha Vantage,该公司提供实时股票、外汇和加密货币市场数据的 Python API,以及技术指标。

  • 提供数据访问的众包投资公司包括 Quantopian 以及于 2018 年 3 月推出的 Alpha Trading Labs,后者提供 HFT 基础设施和数据。

如何处理基础数据

基础数据涉及确定证券价值的经济驱动因素。数据的性质取决于资产类别:

  • 对于股票和公司信用,它包括公司财务状况,以及行业和整体经济数据。

  • 对于政府债券,它包括国际宏观数据和外汇。

  • 对于大宗商品,它包括特定资产的供需决定因素,例如作物的天气数据。

我们将专注于美国的股票基础数据,因为数据更易于获取。全球有超过 13,000 家公开公司,每年生成超过 200 万页的年度报告和超过 30,000 小时的盈利电话。在算法交易中,基础数据和从这些数据中衍生的特征可以直接用于产生交易信号,例如,作为价值指标,并且是预测模型的基本输入,包括 ML 模型。

财务报表数据

美国证券交易委员会(SEC)要求美国发行人,即上市公司和证券,包括共同基金,在提交三份季度财务报表(Form 10-Q)和一份年度报告(Form 10-K)之外,还需遵守各种其他监管文件要求。

自 1990 年代初以来,SEC 通过其Electronic Data Gathering, Analysis, and RetrievalEDGAR)系统提供了这些文件。它们构成了对股票和其他证券进行基本分析的主要数据来源,例如企业信用,其价值取决于发行人的业务前景和财务状况。

自动化处理 - XBRL

自 SEC 推出XBRL以来,对监管文件的自动化分析变得更加容易。这是一种免费的、开放的、全球标准的业务报告的电子表示和交换。XBRL 基于 XML;它依赖于定义报告元素含义的分类法,并映射到在报告的电子版本中突出显示相应信息的标签。其中一种分类法代表了美国的通用会计准则GAAP)。

SEC 于 2005 年推出了自愿提交 XBRL 文件,以应对会计丑闻,随后于 2009 年要求所有提交者采用这种格式,并继续将强制性覆盖范围扩展到其他监管申报。SEC 维护了一个网站,列出了塑造不同申报内容的当前术语表,并可用于提取特定项目。

以下数据集以扁平化数据格式提供从提交给委员会的 EX-101 附件中提取的信息,以帮助用户消费数据进行分析。数据反映了从 XBRL 标记的财务报表中选择的信息。目前包括来自季度和年度财务报表的数字数据,以及某些附加字段,例如,标准工业分类 (SIC)。

有几种途径可以跟踪和访问报告给美国证监会(SEC)的基本数据:

  • 作为 EDGAR 公开传播服务 (PDS)的一部分,可按费用获取接受申报的电子数据流。

  • SEC 每 10 分钟更新 RSS 订阅源,其中列出了结构化披露提交。

  • 有用于通过 FTP 检索所有申报的公共索引文件,以进行自动处理。

  • 财务报表(及注释)数据集包含了所有财务报表和附注的解析 XBRL 数据。

SEC 还发布包含通过 SEC.gov 对 EDGAR 申报的互联网搜索流量的日志文件,尽管有 6 个月的延迟。

构建基本数据时间序列

财务报表和注释数据集中的数据范围包括从主要财务报表(资产负债表、损益表、现金流量表、权益变动表和综合收益表)和这些报表的附注中提取的数字数据。可用数据早在 2009 年就已经存在。

提取财务报表和注释数据集

以下代码下载并提取给定季度范围内财务报表和注释 (FSN) 数据集中包含的所有历史申报(有关更多详细信息,请参阅edgar_xbrl.ipynb):

SEC_URL = 'https://www.sec.gov/files/dera/data/financial-statement-and-notes-data-sets/'
first_year, this_year, this_quarter = 2014, 2018, 3
past_years = range(2014, this_year)
filing_periods = [(y, q) for y in past_years for q in range(1, 5)]
filing_periods.extend([(this_year, q) for q in range(1, this_quarter + 
                                                    1)])
for i, (yr, qtr) in enumerate(filing_periods, 1):
    filing = f'{yr}q{qtr}_notes.zip'
    path = data_path / f'{yr}_{qtr}' / 'source'
    response = requests.get(SEC_URL + filing).content
    with ZipFile(BytesIO(response)) as zip_file:
        for file in zip_file.namelist():
            local_file = path / file
            with local_file.open('wb') as output:
                for line in zip_file.open(file).readlines():
                    output.write(line) 

数据相当庞大,为了比原始文本文件允许的更快访问,最好将文本文件转换为二进制、Parquet 列格式(请参考本章后面关于使用 pandas 进行高效数据存储的部分,以了解与 pandas DataFrames 兼容的各种数据存储选项的性能比较):

for f in data_path.glob('**/*.tsv'):
    file_name = f.stem  + '.parquet'
    path = Path(f.parents[1]) / 'parquet'
    df = pd.read_csv(f, sep='\t', encoding='latin1', low_memory=False)
    df.to_parquet(path / file_name) 

对于每个季度,FSN 数据被组织成八个文件集,其中包含有关提交、数字、术语标签、展示等的信息。每个数据集由行和字段组成,并以制表符分隔的文本文件形式提供:

文件数据集描述
SUB提交标识每个 XBRL 提交的公司、表单、日期等
TAG标签定义和解释每个术语标签
DIM维度为数字和纯文本数据添加细节
NUM数值每个文件中不同数据点的一行
TXT纯文本包含所有非数值 XBRL 字段
REN渲染在 SEC 网站上渲染的信息
PRE展示主要报表中标签和数字展示的详细信息
CAL计算展示标签之间的算术关系
检索所有季度苹果申报文件

提交的数据集包含检索申报所需的唯一标识符:中心索引码CIK)和访问编号adsh)。以下显示了关于苹果 2018Q1 10-Q 申报的一些信息:

apple = sub[sub.name == 'APPLE INC'].T.dropna().squeeze()
key_cols = ['name', 'adsh', 'cik', 'name', 'sic', 'countryba',  
            'stprba', 'cityba', 'zipba', 'bas1', 'form', 'period', 
            'fy', 'fp', 'filed']
apple.loc[key_cols]
name                    APPLE INC
adsh                    0000320193-18-000070
cik                     320193
name                    APPLE INC
sic                     3571
countryba               US
stprba                  CA
cityba                  CUPERTINO
zipba                   95014
bas1                    ONE APPLE PARK WAY
form                    10-Q
period                  20180331
fy                      2018
fp                      Q2
filed                   20180502 

使用 CIK,我们可以识别出苹果可用的所有历史季度申报,并将此信息结合起来获取 26 份 10-Q 表单和 9 份年度 10-K 表单:

aapl_subs = pd.DataFrame()
for sub in data_path.glob('**/sub.parquet'):
    sub = pd.read_parquet(sub)
    aapl_sub = sub[(sub.cik.astype(int) == apple.cik) & 
                   (sub.form.isin(['10-Q', '10-K']))]
    aapl_subs = pd.concat([aapl_subs, aapl_sub])
aapl_subs.form.value_counts()
10-Q    15
10-K     4 

有了每个文件的存取编号,现在我们可以依赖于分类法,从NUMTXT文件中选择适当的 XBRL 标签(列在TAG文件中),以获取感兴趣的数值或文本/脚注数据点。

首先,让我们提取所有可用的 19 份苹果申报中的数值数据:

aapl_nums = pd.DataFrame()
for num in data_path.glob('**/num.parquet'):
    num = pd.read_parquet(num).drop('dimh', axis=1)
    aapl_num = num[num.adsh.isin(aapl_subs.adsh)]
    aapl_nums = pd.concat([aapl_nums, aapl_num])
aapl_nums.ddate = pd.to_datetime(aapl_nums.ddate, format='%Y%m%d')
aapl_nums.shape
(28281, 16) 
构建一个价格/收益时间序列

总共,9 年的申报历史为我们提供了超过 28,000 个数值。我们可以选择一个有用的字段,比如每股收益EPS),然后将其与市场数据相结合,计算流行的市盈率P/E)估值比。

但是我们需要注意,苹果于 2014 年 6 月 4 日将其股票进行了 7:1 的拆分,并调整分拆前的每股收益值,以使其与价格数据相比可比,这在其 调整后 形式中考虑到了这些变化。以下代码块向您展示了如何调整收益数据:

field = 'EarningsPerShareDiluted'
stock_split = 7
split_date = pd.to_datetime('20140604')
# Filter by tag; keep only values measuring 1 quarter
eps = aapl_nums[(aapl_nums.tag == 'EarningsPerShareDiluted')
                & (aapl_nums.qtrs == 1)].drop('tag', axis=1)
# Keep only most recent data point from each filing
eps = eps.groupby('adsh').apply(lambda x: x.nlargest(n=1, columns=['ddate']))
# Adjust earnings prior to stock split downward
eps.loc[eps.ddate < split_date,'value'] = eps.loc[eps.ddate < 
        split_date, 'value'].div(7)
eps = eps[['ddate', 'value']].set_index('ddate').squeeze()
# create trailing 12-months eps from quarterly data
eps = eps.rolling(4, min_periods=4).sum().dropna() 

我们可以使用 Quandl 获取自 2009 年以来的苹果股票价格数据:

import pandas_datareader.data as web
symbol = 'AAPL.US'
aapl_stock = web.DataReader(symbol, 'quandl', start=eps.index.min())
aapl_stock = aapl_stock.resample('D').last() # ensure dates align with 
                                               eps data 

现在我们有了数据,可以计算整个时期的滚动 12 个月 P/E 比率:

pe = aapl_stock.AdjClose.to_frame('price').join(eps.to_frame('eps'))
pe = pe.fillna(method='ffill').dropna()
pe['P/E Ratio'] = pe.price.div(pe.eps)
axes = pe.plot(subplots=True, figsize=(16,8), legend=False, lw=2); 

我们从前面的代码中得到以下图表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.11:来自 EDGAR 申报的滚动市盈率

其他基本数据来源

还有许多其他的基本数据来源。许多可以使用之前介绍的pandas_datareader模块访问。还可以直接从某些组织获取额外的数据,例如 IMF、世界银行或世界各地的主要国家统计机构(参见 GitHub 上的参考资料)。

pandas-datareader – 宏观和行业数据

pandas-datareader 库根据在前一节关于市场数据介绍的约定提供访问。它涵盖了众多全球基本宏观和行业数据源的 API,包括以下内容:

  • Kenneth French 的数据库:捕获关键风险因素的投资组合的市场数据,如规模、价值和动量因素,按行业细分(参见 第四章金融特征工程 - 如何研究阿尔法因子

  • 圣路易斯联邦储备(FRED):美国经济和金融市场的联邦储备数据

  • 世界银行:关于长期、低频经济和社会发展以及人口统计的全球数据库

  • OECD:类似于世界银行数据,针对经济合作与发展组织成员国

  • Enigma:各种数据集,包括替代来源

  • 欧洲统计局:欧盟经济、社会和人口数据重点关注

使用 pandas 进行高效数据存储

在本书中,我们将使用许多不同的数据集,并且值得比较主要格式的效率和性能。特别是,我们将比较以下内容:

  • CSV:逗号分隔的标准平面文本文件格式。

  • HDF5:分层数据格式,最初由国家超级计算应用中心开发。对于数字数据,它是一种快速且可扩展的存储格式,可通过 PyTables 库在 pandas 中使用。

  • Parquet:Apache Hadoop 生态系统的一部分,二进制、列存储格式,提供高效的数据压缩和编码,由 Cloudera 和 Twitter 开发。它可通过由 pandas 原始作者 Wes McKinney 领导的 pyarrow 库提供,后者是 pandas 的原始作者。

storage_benchmark.ipynb 笔记本使用可以配置为包含数字或文本数据,也可以两者兼有的测试 DataFrame 来比较前述库的性能。对于 HDF5 库,我们测试了固定格式和表格式。表格格式允许查询并且可以附加。

以下图表说明了在 100,000 行数据中读取和写入性能,数据包括 1,000 列随机浮点数和 1,000 列随机 10 个字符的字符串,或仅包含 2,000 列浮点数(采用对数刻度):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2.12:存储基准测试

左侧面板显示,对于纯数字数据,HDF5 格式远远表现最佳,而表格格式在 1.6 GB 的情况下也与 CSV 具有最小的内存占用。fixed 格式使用的空间是它的两倍,而 parquet 格式使用了 2 GB。

对于数字和文本数据的混合,Parquet 是读写操作的最佳选择。HDF5 在 读取 方面相对于 CSV 有优势,但在 写入 方面较慢,因为它将文本数据进行了 pickle 化。

该笔记本说明了如何使用 %%timeit 单元格魔术配置、测试和收集计时,并同时演示了使用这些存储格式所需的相关 pandas 命令的用法。

概要

本章介绍了组成大多数交易策略骨干的市场和基础数据源。您了解到了访问这些数据的各种方式以及如何预处理原始信息,以便您可以开始使用我们即将介绍的 ML 技术提取交易信号。

在下一章, 在继续设计和评估交易策略以及使用机器学习模型之前,我们需要介绍近年来出现的替代数据集,这些数据集对算法交易中机器学习的流行起到了重要推动作用。

第三章:金融领域的替代数据 - 类别和用例

前一章涵盖了与市场和基本数据的工作,这些一直是交易策略的传统驱动因素。在本章中,我们将快进到最近出现的更多种类的更多样化的数据源作为离散和算法策略的燃料。它们的异质性和新颖性激发了替代数据的标签,并创造了一个迅速增长的提供者和服务行业。

这一趋势背后有一个熟悉的故事:在互联网和移动网络的爆炸性增长推动下,数字数据在新数据源的处理、存储和分析技术的进步中呈指数级增长。数字数据的可用性和管理能力的指数增长反过来推动了机器学习(ML)的显著性能改进,推动了包括投资行业在内的各行业的创新。

数据革命的规模是非凡的:仅过去两年就见证了世界上现有所有数据的 90%的创建,到 2020 年,全球 77 亿人口中的每个人预计每天每秒会产生 1.7MB 的新信息。另一方面,2012 年时,只有 0.5%的数据被分析和使用,而到 2020 年,有 33%的数据被认为具有价值。数据可用性与使用之间的差距可能会迅速缩小,因为全球对分析的投资预计将在 2020 年之前超过 2100 亿美元,而价值创造潜力则是数倍于此。

本章解释了个人、业务流程和传感器如何生成替代数据。它还提供了一个框架来导航和评估用于投资目的的不断增长的替代数据供应。它展示了从获取到预处理和存储的工作流程,使用 Python 处理通过网络爬虫获得的数据,为机器学习的应用做好准备。它最后提供了一些来源、提供者和应用的示例。

本章将涵盖以下主题:

  • 替代数据革命释放了哪些新的信息来源

  • 个人、业务流程和传感器如何生成替代数据

  • 评估用于算法交易的不断增长的替代数据供应

  • 在 Python 中使用替代数据,例如通过网络爬虫进行爬取

  • 替代数据的重要类别和提供者

您可以在 GitHub 存储库的相应目录中找到本章的代码示例和额外资源的链接。笔记本包括图像的彩色版本。

替代数据革命

数字化、网络化和存储成本的暴跌带来的数据洪流已经导致了可用于预测分析的信息性质发生了深刻的定性变化,通常由五个“V”来概括:

  • 容量:作为在线和离线活动、交易、记录和其他来源的副产品,产生、收集和存储的数据量呈数量级增长。随着分析和存储能力的增长,数据量持续增长。

  • 速度:数据生成、传输和处理以便以接近实时或实时的速度可用。

  • 多样性:数据以不再限于结构化、表格形式的格式组织,如 CSV 文件或关系数据库表。而是,新的来源产生了半结构化格式,如 JSON 或 HTML,以及非结构化内容,包括原始文本、“图片”?和音频或视频数据,为将数据适应机器学习算法增添了新的挑战。

  • 真实性:数据来源和格式的多样性使得验证数据信息内容的可靠性变得更加困难。

  • 价值:确定新数据集的价值可能比以往更加耗时、耗资源,也更加不确定。

对于算法交易,如果新数据来源提供了无法从传统来源获取的信息或提供了更早的访问机会,则这些新数据来源将提供信息优势。顺应全球趋势,投资行业正迅速从市场和基本数据扩展到替代来源,通过信息优势实现阿尔法收益。数据、技术能力和相关人才的年度支出预计将从目前的 30 亿美元以每年 12.8%的速度增长至 2020 年。

如今,投资者可以实时获取宏观或特定公司的数据,而这些数据在历史上只能以更低的频率获得。新数据来源的用例包括以下内容:

  • 在线价格数据可用于衡量一组代表性商品和服务的通货膨胀。

  • 店铺访问或购买数量允许对公司或行业特定销售或经济活动进行实时估计。

  • 卫星图像可以在其他地方获得这些信息之前,揭示农业产量,或者矿山或油井上的活动。

随着大数据集的标准化和采用的推进,传统数据中所包含的信息可能会失去大部分预测价值。

此外,处理和整合多样化数据集并应用机器学习的能力可实现复杂的见解。过去,量化方法依赖于使用历史数据对公司进行排名的简单启发式方法,例如市净率,而机器学习算法会综合新指标并学习和调整这些规则,同时考虑到不断发展的市场数据。这些见解创造了捕捉价值、动能、质量和情绪等经典投资主题的新机会:

  • 动能:机器学习可以识别资产暴露于市场价格波动、行业情绪或经济因素。

  • 价值:算法可以分析大量的经济和行业特定的结构化和非结构化数据,超越财务报表,来预测公司的内在价值。

  • 质量:综合数据的复杂分析可以评估客户或员工的评论、电子商务或应用流量,以识别市场份额或其他潜在收益质量驱动因素的增长。

  • 情感:对新闻和社交媒体内容的实时处理和解释使 ML 算法能够快速检测到新兴情感,并将信息从各种来源综合成更为连贯的整体图景。

然而,在实践中,包含有价值信号的数据通常并不是自由可用的,并且通常是为了除交易目的之外的其他目的而生成的。因此,需要对替代数据集进行彻底评估、昂贵的获取、谨慎的管理和复杂的分析,以提取可交易信号。

替代数据来源

替代数据集由许多来源生成,但可以在高层次上分类为主要由以下方面产生:

  • 在社交媒体上发布、评价产品或使用搜索引擎的个人

  • 记录商业交易(特别是信用卡支付)或作为中介捕获供应链活动的企业

  • 传感器,除其他功能外,通过卫星图像或安全摄像头捕获经济活动,或通过诸如手机基站等移动模式

替代数据的性质在不断迅速演变,因为新的数据来源变得可用,并且以前被标记为“替代”的来源成为主流的一部分。例如,波罗的海干散货指数BDI)汇集了数百家航运公司的数据,以近似干散货船的供需,并且现在可在彭博终端上获得。

替代数据包括原始数据以及已经聚合或以某种形式加工以增加价值的数据。例如,一些提供商旨在提取可交易信号,如情感评分。我们将在第四章金融特征工程-如何研究阿尔法因子中讨论各种类型的提供商。

替代数据源在决定其价值或信号内容的关键方面存在差异。在查看本节的主要来源后,我们将在下一节中讨论这些方面。

个人

个人通过在线活动自动生成电子数据,以及通过线下活动,后者通常以电子方式捕获,并经常与在线身份相关联。个人生成的数据通常以文本、图像或视频格式呈现,通过多个平台传播,包括:

  • 社交媒体帖子,例如在 Twitter、Facebook 或 LinkedIn 等通用站点上的意见或反应,或在 Glassdoor 或 Yelp 等商业评论站点上的评论

  • 反映对产品的兴趣或感知的电子商务活动,如亚马逊或 Wayfair 等网站上的活动

  • 使用 Google 或 Bing 等平台的搜索引擎活动

  • 移动应用程序的使用情况、下载量和评论

  • 个人数据,如消息流量

社交媒体情感分析变得非常流行,因为它可应用于个别股票、行业篮子或市场指数。最常见的来源是 Twitter,其次是各种新闻供应商和博客网站。供应竞争激烈,价格较低,因为它通常是通过日益成熟的网络抓取获得的。可靠的社交媒体数据集通常包括博客、推文或视频,因为消费者最近才大规模采用这些工具,因此通常历史不到 5 年。相比之下,搜索历史可追溯到 2004 年。

业务流程

企业和公共实体产生并收集许多有价值的替代数据源。业务流程产生的数据通常比个人生成的数据更具结构性。作为活动的领先指标,它对于以往频率低得多的活动非常有效。

由业务流程产生的数据包括:

  • 可能可从处理器和金融机构购买的支付卡交易数据

  • 普通数字化活动或记录保留产生的公司废弃数据,如银行记录、收银员扫描数据或供应链订单

  • 贸易流量和市场微观结构数据(如 L2 和 L3 订单簿数据,由第二章“市场和基本数据 - 来源和技术”中的纳斯达克 ITCH tick 数据示例说明)

  • 由信用评级机构或金融机构监控的公司付款,以评估流动性和信用状况

信用卡交易和公司的废弃数据,如销售数据,是最可靠和具有预测性的数据集之一。信用卡数据可追溯到约 10 年的历史,而且在不同的滞后期几乎接近实时,而公司收入则是以 2.5 周的滞后期季度报告。公司废弃数据的时间跨度和报告滞后期因来源而异。市场微观结构数据有超过 15 年的历史,而销售端流量数据通常只有不到 5 年的一致历史。

传感器

嵌入在各种设备中的网络传感器是增长最快的数据来源之一,这是由智能手机的普及和卫星技术成本的降低推动的。

这一类替代数据通常非常无结构,通常比个人或业务流程产生的数据量大得多,并且具有更加严峻的处理挑战。该类别中的主要替代数据源包括:

  • 卫星成像用于监测经济活动,如建筑、航运或商品供应

  • 地理定位数据用于跟踪零售店的交通情况,例如利用志愿者智能手机数据,或者在船只或卡车上的运输路线上。

  • 摄像机位于感兴趣的位置

  • 天气和污染传感器

物联网IoT)将通过将网络微处理器嵌入个人和商业电子设备(例如家用电器、公共空间和工业生产过程)进一步加速这类替代数据的大规模收集。

基于传感器的替代数据通常具有 3 至 4 年的历史,其中包括卫星图像、移动应用程序使用情况或手机位置跟踪。

卫星

发射地球观测卫星所需的资源和时间表已经大幅下降;现在,将一个小卫星作为次要有效载荷送入低地球轨道的成本已经降至约 10 万美元左右,而不再需要数千万美元和数年的准备时间。因此,公司可以利用整个卫星舰队实现更高频率的覆盖(目前约为每日一次)特定位置。

用例包括监测可通过空中覆盖范围捕捉的经济活动,例如农业和矿产生产和装运,或者商业或住宅建筑物或船只的建造;工业事故,例如火灾;或感兴趣地点的车辆和人流量。相关的传感器数据由用于农业的无人机提供,利用红外光监测作物。

在卫星图像数据可靠用于机器学习模型之前,通常需要解决几个挑战。除了进行大量的预处理外,还需要考虑诸如云覆盖和节假日季节效应等天气条件。卫星可能还只提供特定位置的不规则覆盖,这可能会影响预测信号的质量。

地理定位数据

地理定位数据是传感器生成的另一种快速增长的替代数据类别。熟悉的来源是智能手机,个人可以通过应用程序自愿分享其地理位置,或者通过 GPS、CDMA 或 Wi-Fi 等无线信号测量感兴趣地点周围的人流量,例如商店、餐馆或活动场所。

此外,越来越多的机场、购物中心和零售店安装了跟踪顾客数量和动向的传感器。虽然最初部署这些传感器的动机通常是为了衡量营销活动的影响,但产生的数据也可用于估算人流量或销售额。用于捕捉地理位置数据的传感器包括 3D 立体视频和热成像,这降低了隐私顾虑,但对移动物体效果很好。还有附在天花板上的传感器,以及压力敏感的垫子。一些供应商结合使用多种传感器,包括视觉、音频和手机定位,以全面记录购物者的旅程,其中不仅包括访问次数和持续时间,还延伸到转化和重复访问的测量。

评估替代数据的标准

替代数据的最终目标是在寻找产生 alpha(即正的、不相关的投资回报)的交易信号方面提供信息优势。在实践中,从替代数据集中提取的信号可以单独使用,也可以作为量化策略的一部分与其他信号结合使用。如果基于单一数据集的策略生成的夏普比率足够高,独立使用是可行的,但在实践中很少见。(有关信号测量和评估的详细信息,请参阅第四章金融特征工程 - 如何研究 Alpha 因子。)

量化公司正在构建可能在个体上是弱信号但组合起来可以产生有吸引力回报的 alpha 因子库。正如第一章用于交易的机器学习 - 从构想到执行中所强调的,投资因子应该基于基本的经济原理;否则,它们更有可能是对历史数据的过度拟合的结果,而不是在新数据上持久存在并产生 alpha。

由于竞争而导致的信号衰减是一个严重问题,在替代数据生态系统不断发展的情况下,许多数据集不太可能保留有意义的夏普比率信号。延长替代数据集信号内容半衰期的有效策略包括独家协议,或者专注于提高数据处理难度以增加进入壁垒的数据集。

可以根据替代数据集的信号内容质量、数据的定性特征以及各种技术方面来评估替代数据集。

信号内容的质量

可以针对目标资产类别、投资风格、与传统风险溢价的关系以及最重要的是其 alpha 内容来评估信号内容。

资产类别

大多数替代数据集包含与股票和商品直接相关的信息。2006 年 Zillow 成功地开创了价格估算之后,针对房地产投资的有趣数据集也在不断增加。

企业信用的替代数据正在增长,因为正在开发监控企业支付的替代来源,包括针对较小企业的数据。围绕固定收益和利率预测的数据是一个较新的现象,但随着更多产品销售和价格信息的大规模获取,它继续增加。

投资风格

大多数数据集都专注于特定行业和股票,因此自然吸引长短股权投资者。随着替代数据收集的规模和范围不断扩大,替代数据可能也会变得与宏观主题的投资者相关,例如消费信贷,新兴市场活动和商品趋势。

一些代表更广泛经济活动或消费者情绪的替代数据集可用作传统市场风险度量的代理。相比之下,捕捉新闻的信号可能更相关于使用量化策略的高频交易员,在短期内进行交易。

风险溢价

一些替代数据集,如信用卡支付或社交媒体情绪,已被证明产生的信号与传统风险溢价在股票市场上的相关性很低(低于 5%),如价值,动量和波动性质量。因此,将从这些替代数据派生的信号与基于传统风险因子的算法交易策略相结合,可以成为构建更多样化风险溢价组合的重要组成部分。

Alpha 内容和质量

正义投资替代数据集的投资所需的信号强度自然取决于其成本,而替代数据的价格差异很大。社会情绪得分的数据可以用几千美元或更少的价格获得,而全面及时的信用卡支付数据集的成本可能每年达数百万美元。

我们将详细探讨如何使用历史数据评估由替代数据驱动的交易策略,即所谓的回测,以估算数据集中包含的 alpha 数量。在个别情况下,数据集可能包含足够的 alpha 信号以单独驱动策略,但更典型的是结合使用各种替代和其他数据源。在这些情况下,数据集允许提取产生小正夏普比率的弱信号,这些信号单独不会获得资本配置,但与类似其他信号集成后可以提供组合级策略。然而,这并不是保证,因为也有许多替代数据集不包含任何 alpha 内容。

除了评估数据集的 alpha 内容之外,评估信号的增量程度或正交程度也很重要,即数据集的唯一性或已被其他数据捕捉的程度——在后一种情况下,比较这种类型信号的成本也很重要。

最后,评估依赖给定策略的潜在容量是至关重要的,即可以分配的资金数量,而不会破坏其成功。这是因为容量限制会使数据成本更难以收回。

数据质量

数据集的质量是另一个重要的标准,因为它影响了分析和货币化所需的工作量,以及它包含的预测信号的可靠性。质量方面包括数据频率和其可用历史的长度,其包含信息的可靠性或准确性,其与当前或潜在未来法规的符合程度,以及其使用的独占性。

法律和声誉风险

使用替代数据集可能会带来法律或声誉风险,尤其是当它们包含以下内容时:

  • 重大非公开信息 (MNPI),因为它涉及内幕交易规定的侵犯

  • 个人可识别信息 (PII),主要是因为欧盟已经颁布了通用数据保护条例 (GDPR)

因此,法律和合规要求需要进行彻底审查。当数据提供者也是积极根据数据集交易的市场参与者时,可能会存在利益冲突。

独占性

替代数据集包含充分预测信号以推动策略的可能性,其与其可用性和处理的便捷性成反比,对于一个有意义的时间段内具有较高的夏普比率。换句话说,数据越独占性越强,越难处理,具有 alpha 内容的数据集驱动策略的机会就越好,而不会遭受快速信号衰减的影响。

提供标准财务比率的公开基本数据包含较少的 alpha,并且对于独立策略而言不具吸引力,但它可能有助于多样化风险因素组合。大型复杂数据集需要更长时间才能被市场吸收,并且新数据集会经常出现。因此,评估其他投资者对数据集的熟悉程度以及数据提供者是否是此类信息的最佳来源至关重要。

独占性或是早期采用新数据集的其他好处可能在企业开始销售其为其他目的生成的耗尽数据时出现。这是因为可能有可能影响数据的收集或筛选方式,或者协商条件以限制竞争对手的访问,至少在一定时间内。

时间范围

为了测试数据集在不同场景中的预测能力,更广泛的历史记录是非常可取的。可用性在几个月到几十年之间变化很大,并且对基于数据构建和测试的交易策略的范围有重要影响。在介绍主要数据来源时,我们提到了不同数据集的时间范围。

频率

数据的频率决定了新信息何时可用,以及在给定时期内预测信号的差异化程度。它还影响投资策略的时间范围,范围从日内到日常、每周,甚至更低的频率。

可靠性

当然,数据准确反映其意图的程度,以及这可以被验证的程度,是一个重要问题,应该通过彻底的审计来验证。这适用于原始数据和处理后的数据,其中用于提取或聚合信息的方法需要进行分析,考虑到提议收购的成本效益比。

技术方面

技术方面涉及延迟或报告延迟,以及数据的可用格式。

延迟

数据提供商通常会分批提供资源,由于数据的收集、后续处理和传输,以及监管或法律约束,可能会导致延迟。

格式

数据以广泛的格式提供,具体取决于来源。处理过的数据将以用户友好的格式提供,并通过强大的 API 轻松集成到现有系统或查询中。在另一端是庞大的数据源,例如视频、音频或图像数据,或专有格式,这需要更多的技能来准备分析,但也为潜在竞争对手提供更高的进入障碍。

另类数据市场

2018 年,投资行业在数据服务上的支出估计达到了 20-30 亿美元,预计这一数字将以两位数的年增长率增长,与其他行业保持一致。这些支出包括另类数据的获取、相关技术的投资以及合格人才的招聘。

安永(Ernst & Young)的一项调查显示,2017 年另类数据的采用率显著增加;例如,43%的基金正在使用抓取的网络数据,而近 30%的基金正在尝试卫星数据(见图 3.1)。根据迄今为止的经验,基金经理认为抓取的网络数据和信用卡数据最具洞察力,而与之相反的是地理位置和卫星数据,约 25%的人认为信息较少:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3.1:另类数据的有用性和使用情况(来源:安永,2017 年)

鉴于这个新行业的快速增长,另类数据提供商市场相当分散。摩根大通列出了 500 多家专业数据公司,而AlternativeData.org列出了 300 多家。提供商扮演多种角色,包括顾问、聚合器和技术解决方案的中间商;卖方支持以各种格式提供数据,从原始数据到半加工数据或从一个或多个来源提取的某种形式的信号。

我们将重点介绍主要类别的规模,并概述几个突出的例子,以说明它们的多样性。

数据提供商和用例

AlternativeData.org(由提供商 YipitData 支持)列出了几个类别,可以作为各种数据提供商领域活动的粗略代理。社交情绪分析是迄今为止最大的类别,而卫星和地理位置数据近年来增长迅速:

产品类别# 提供商
社交情绪48
卫星26
地理位置22
网页数据和流量22
基础设施和接口20
顾问18
信用卡和借记卡使用14
数据经纪人10
公共数据10
应用使用7
电子邮件和消费者收据6
卖方6
天气4
其他87

以下简要示例旨在说明服务提供商和潜在用例的广泛范围。

社交情绪数据

社交情绪分析与 Twitter 数据密切相关。 Gnip 是一个早期的社交媒体聚合器,通过 API 从众多网站提供数据,并于 2014 年以 1.34 亿美元被 Twitter 收购。搜索引擎是另一个来源,在研究人员在《自然》杂志上发表,称基于 Google Trends 等诸如债务等术语的投资策略可用于较长时期的盈利交易策略时变得突出(Preis、Moat 和 Stanley 2013 年)。

Dataminr

Dataminr 成立于 2009 年,根据与 Twitter 的独家协议提供社交情绪和新闻分析。该公司是较大的另类提供商之一,于 2018 年 6 月通过 Fidelity 领投筹集了额外的 3.91 亿美元,估值达到 16 亿美元,使总融资额达到 5.69 亿美元。它强调利用机器学习从社交媒体源提取的实时信号,并为广泛的客户提供服务,包括不仅限于买方和卖方投资公司,还包括新闻机构和公共部门。

StockTwits

StockTwits 是一个社交网络和微博平台,数十万投资专业人士通过 StockTwits 分享信息和交易想法。这些被大量金融网站和社交媒体平台的用户所观看。这些数据可以被利用,因为它可能反映投资者情绪,或者本身推动交易,从而影响价格。Nasseri、Tucker 和 de Cesare(2015)基于选定特征构建了交易策略。

RavenPack

RavenPack 分析大量多样化的非结构化文本数据,生成结构化指标,包括情绪分数,旨在提供与投资者相关的信息。底层数据源涵盖高级新闻、监管信息、新闻稿和超过 19,000 个网络出版物。J.P. 摩根基于情绪分数测试了基于主权债券和股票的多头多空策略,并取得了积极的结果,与传统风险溢价相关性低(Kolanovic 和 Krishnamachari,2017)。

卫星数据

成立于 2010 年的 RS Metrics,通过卫星、无人机和飞机三角测量地理空间数据,重点关注金属和大宗商品、房地产和工业应用。该公司基于自己的高分辨率卫星提供信号、预测分析、警报和最终用户应用。应用案例包括估算特定连锁店或商业地产的零售流量,以及某些常见金属的生产和储存,或相关生产地点的就业情况。

地理位置数据

成立于 2015 年的 Advan 为对冲基金客户提供从手机流量数据中派生的信号,目标是在美国和欧盟的各个部门中覆盖 1,600 个股票。该公司使用应用程序收集数据,在用户明确同意的情况下在智能手机上安装地理位置代码,并通过几个渠道(如 Wi-Fi、蓝牙和蜂窝信号)跟踪位置以增强准确性。使用案例包括估算物理店面的客流量,进而可以作为预测交易公司的总收入的模型的输入。

电子收据数据

Eagle Alpha 提供包括大量在线交易数据在内的服务,使用电子收据,涵盖超过 5,000 家零售商,包括按 SKU 分类的 53 个产品组的交易数据。根据 J.P. 摩根的分析,涵盖了 2013-16 年的时间序列数据,覆盖了整个样本期间活跃的一组不变用户。数据集包含每个周期的总体支出、订单数和独立买家数(Kolanovic 和 Krishnamachari,2017)。

处理替代数据

我们将说明使用网络抓取获取替代数据,首先针对 OpenTable 餐厅数据,然后转向 Seeking Alpha 托管的盈利电话会议记录。

抓取 OpenTable 数据

另类数据的典型来源是评论网站,如 Glassdoor 或 Yelp,这些网站利用员工评论或客户评价传达内部见解。显然,用户贡献的内容并不能反映一个代表性观点,而是受到严重的选择偏见的影响。例如,我们将在 第十四章 用于交易的文本数据 - 情感分析 中查看 Yelp 评论,并发现比预期更多的五星级评价,包括非常积极和非常消极的评价。尽管如此,这些数据可以为旨在预测企业前景或市场价值相对于竞争对手或随时间获取交易信号的 ML 模型提供宝贵的输入。

需要从 HTML 源代码中提取数据,除非存在任何法律障碍。为了说明 Python 提供的网页抓取工具,我们将从 OpenTable 检索有关餐厅预订的信息。此类数据可以用于预测地理位置、房地产价格或餐厅连锁店营收的经济活动。

使用 Requests 和 BeautifulSoup 解析 HTML 数据

在本节中,我们将请求并解析 HTML 源代码。我们将使用 Requests 库发出 超文本传输协议 (HTTP) 请求并检索 HTML 源代码。然后,我们将依赖于 Beautiful Soup 库,它使解析 HTML 标记代码并提取我们感兴趣的文本内容变得很容易。

但是,我们会遇到一个常见的障碍:网站可能仅在初始页面加载后使用 JavaScript 从服务器请求某些信息。因此,直接的 HTTP 请求将不会成功。为了绕过这种类型的保护,我们将使用一个无界面浏览器,它将像浏览器一样检索网站内容:

from bs4 import BeautifulSoup
import requests
# set and request url; extract source code
url = https://www.opentable.com/new-york-restaurant-listings
html = requests.get(url)
html.text[:500]
' <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE"/> <title>Restaurant Reservation Availability</title> <meta name="robots" content="noindex" > </meta> <link rel="shortcut icon" href="//components.otstatic.com/components/favicon/1.0.4/favicon/favicon.ico" type="image/x-icon"/><link rel="icon" href="//components.otstatic.com/components/favicon/1.0.4/favicon/favicon-16.png" sizes="16x16"/><link rel=' 

现在,我们可以使用 Beautiful Soup 解析 HTML 内容,然后查找我们通过检查源代码获得的与餐厅名称相关的所有 span 标签,rest-row-name-text(请参阅 GitHub 存储库,获取与检查网站源代码相关的链接指示):

# parse raw html => soup object
soup = BeautifulSoup(html.text, 'html.parser')
# for each span tag, print out text => restaurant name
for entry in soup.find_all(name='span', attrs={'class':'rest-row-name-text'}):
    print(entry.text)
Wade Coves
Alley
Dolorem Maggio
Islands
... 

一旦您确定了感兴趣的页面元素,Beautiful Soup 就会轻松获取其中包含的文本。例如,如果您想获取每家餐厅的价格类别,您可以使用:

# get the number of dollars signs for each restaurant
for entry in soup.find_all('div', {'class':'rest-row-pricing'}):
    price = entry.find('i').text 

但是,当您尝试获取预订数量时,您只会收到一个空列表,因为该站点在初始加载完成后使用 JavaScript 代码请求此信息:

soup.find_all('div', {'class':'booking'})
[] 

这正是我们之前提到的挑战 - 不是将所有内容作为静态页面发送到浏览器中,以便轻松解析,而是 JavaScript 动态加载关键片段。为了获取此内容,我们需要执行 JavaScript,就像浏览器一样 - 这就是 Selenium 的作用。

介绍 Selenium - 使用浏览器自动化

我们将使用浏览器自动化工具 Selenium 操作一个无头 Firefox 浏览器,该浏览器将为我们解析 HTML 内容。

以下代码打开 Firefox 浏览器:

from selenium import webdriver
# create a driver called Firefox
driver = webdriver.Firefox() 

让我们关闭浏览器:

# close it
driver.close() 

现在,我们使用 Selenium 和 Firefox 检索 HTML 源代码,包括动态加载的部分。为此,我们向驱动程序提供 URL,然后使用其 page_source 属性获取完整页面内容,如浏览器中显示的那样。

从这里开始,我们可以借助 Beautiful Soup 解析 HTML,如下所示:

import time, re
# visit the opentable listing page
driver = webdriver.Firefox()
driver.get(url)
time.sleep(1) # wait 1 second
# retrieve the html source
html = driver.page_source
html = BeautifulSoup(html, "lxml")
for booking in html.find_all('div', {'class': 'booking'}):
    match = re.search(r'\d+', booking.text)
    if match:
        print(match.group()) 

构建一个餐厅预订和评级的数据集

现在,您只需要从网站中组合所有有趣的元素,以创建一个特征,您可以在模型中使用该特征来预测地理区域的经济活动,或特定社区的人流量。

使用 Selenium,您可以跟踪到下一页的链接,并快速构建纽约市超过 10,000 家餐厅的数据集,然后您可以定期更新它以跟踪时间序列。

首先,我们设置一个函数,用于解析我们打算爬行的页面内容,使用熟悉的 Beautiful Soup 解析语法:

def parse_html(html):
    data, item = pd.DataFrame(), {}
    soup = BeautifulSoup(html, 'lxml')
    for i, resto in enumerate(soup.find_all('div',
                                           class_='rest-row-info')):
        item['name'] = resto.find('span',
                                 class_='rest-row-name-text').text
        booking = resto.find('div', class_='booking')
        item['bookings'] = re.search('\d+', booking.text).group() \
            if booking else 'NA'
        rating = resto.find('div', class_='star-rating-score')
        item['rating'] = float(rating['aria-label'].split()[0]) \
            if rating else 'NA'
        reviews = resto.find('span', class_='underline-hover')
        item['reviews'] = int(re.search('\d+', reviews.text).group()) \
            if reviews else 'NA'
        item['price'] = int(resto.find('div', class_='rest-row-pricing')
                            .find('i').text.count('$'))
        cuisine_class = 'rest-row-meta--cuisine rest-row-meta-text sfx1388addContent'
        item['cuisine'] = resto.find('span', class_=cuisine_class).text
        location_class = 'rest-row-meta--location rest-row-meta-text sfx1388addContent'
        item['location'] = resto.find('span', class_=location_class).text
        data[i] = pd.Series(item)
    return data.T 

然后,我们启动一个无头浏览器,它会继续为我们点击“下一页”按钮,并捕获每个页面上显示的结果:

restaurants = pd.DataFrame()
driver = webdriver.Firefox()
url = https://www.opentable.com/new-york-restaurant-listings
driver.get(url)
while True:
    sleep(1)
    new_data = parse_html(driver.page_source)
    if new_data.empty:
        break
    restaurants = pd.concat([restaurants, new_data], ignore_index=True)
    print(len(restaurants))
    driver.find_element_by_link_text('Next').click()
driver.close() 

2020 年初的一次样本运行提供了关于 10,000 家餐厅的位置、菜系和价格分类信息。此外,在周一约有 1,750 家餐厅提供当天预订服务,并且大约有 3,500 家餐厅的评分和评论信息。

图 3.2 显示了一个简要摘要:左侧面板显示了前 10 个拥有最多餐厅的地点按价格分类的分布情况。中央面板表明,平均而言,价格较高的餐厅评分更高,右侧面板突出显示评分较高的餐厅接受的预订更多。随时间跟踪这些信息可能是有信息价值的,例如,关于消费者情绪、地点偏好或特定餐厅连锁店的情况:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3.2:OpenTable 数据摘要

网站不断变化,因此此代码可能在某个时候停止工作。为了更新我们的机器人,我们需要识别网站导航的变化,如新的类或 ID 名称,并相应地更正解析器。

使用 Scrapy 和 Splash 进一步实现自动化

Scrapy 是一个强大的库,用于构建跟踪链接、检索内容并以结构化方式存储解析结果的机器人。结合 Splash 无头浏览器,它还可以解释 JavaScript,并成为 Selenium 的高效替代品。

您可以在 01_opentable 目录中使用 scrapy crawl opentable 命令运行蜘蛛,结果将记录在 spider.log 中:

from opentable.items import OpentableItem
from scrapy import Spider
from scrapy_splash import SplashRequest
class OpenTableSpider(Spider):
    name = 'opentable'
    start_urls = ['https://www.opentable.com/new-york-restaurant-
                   listings']
    def start_requests(self):
        for url in self.start_urls:
            yield SplashRequest(url=url,
                                callback=self.parse,
                                endpoint='render.html',
                                args={'wait': 1},
                                )
    def parse(self, response):
        item = OpentableItem()
        for resto in response.css('div.rest-row-info'):
            item['name'] = resto.css('span.rest-row-name-
                                      text::text').extract()
            item['bookings'] = 
                  resto.css('div.booking::text').re(r'\d+')
            item['rating'] = resto.css('div.all-
                  stars::attr(style)').re_first('\d+')
            item['reviews'] = resto.css('span.star-rating-text--review-
                                         text::text').re_first(r'\d+')
            item['price'] = len(resto.css('div.rest-row-pricing > 
                                i::text').re('\$'))
            item['cuisine'] = resto.css('span.rest-row-meta—
                                         cuisine::text').extract()
            item['location'] = resto.css('span.rest-row-meta—
                               location::text').extract()
            yield item 

有许多方法可以从这些数据中提取信息,超出了个别餐厅或连锁店的评论和预订之外。

我们可以进一步收集和地理编码餐厅的地址,例如,将餐厅的实际位置与其他感兴趣的地区(如热门零售点或社区)联系起来,以获得关于特定经济活动方面的见解。如前所述,这些数据与其他信息结合使用将是最有价值的。

爬取和解析盈利电话会议记录

文本数据是一个重要的替代数据来源。文本信息的一个例子是盈利电话会议的记录,执行人员在这些记录中不仅呈现最新的财务结果,还回答金融分析师的问题。投资者利用这些记录来评估情绪变化、特定主题的强调或沟通风格的变化。

我们将说明从流行的交易网站www.seekingalpha.com爬取和解析盈利电话会议记录的过程。与 OpenTable 示例类似,我们将使用 Selenium 访问 HTML 代码,并使用 Beautiful Soup 解析内容。为此,我们首先实例化一个适用于 Firefox 浏览器的 Selenium webdriver实例:

from urllib.parse import urljoin
from bs4 import BeautifulSoup
from furl import furl
from selenium import webdriver
transcript_path = Path('transcripts')
SA_URL = 'https://seekingalpha.com/'
TRANSCRIPT = re.compile('Earnings Call Transcript')
next_page = True
page = 1
driver = webdriver.Firefox() 

然后,我们对记录页面进行迭代,根据我们从检查网站获得的导航逻辑创建 URL。只要我们找到与其他记录相关的超链接,我们就访问 webdriver 的page_source属性,并调用parse_html函数来提取内容:

while next_page:
    url = f'{SA_URL}/earnings/earnings-call-transcripts/{page}'
    driver.get(urljoin(SA_URL, url))
    response = driver.page_source
    page += 1
    soup = BeautifulSoup(response, 'lxml')
    links = soup.find_all(name='a', string=TRANSCRIPT)
    if len(links) == 0:
        next_page = False
    else:
        for link in links:
            transcript_url = link.attrs.get('href')
            article_url = furl(urljoin(SA_URL, 
                           transcript_url)).add({'part': 'single'})
            driver.get(article_url.url)
            html = driver.page_source
            meta, participants, content = parse_html(html)
            meta['link'] = link
driver.close() 

要从非结构化的记录中收集结构化数据,我们除了使用 Beautiful Soup 外,还可以使用正则表达式。

它们使我们能够收集有关盈利电话会议公司和时间的详细信息,还能了解到谁在场,并将陈述归因于分析师和公司代表:

def parse_html(html):
    date_pattern = re.compile(r'(\d{2})-(\d{2})-(\d{2})')
    quarter_pattern = re.compile(r'(\bQ\d\b)')
    soup = BeautifulSoup(html, 'lxml')
    meta, participants, content = {}, [], []
    h1 = soup.find('h1', itemprop='headline').text
    meta['company'] = h1[:h1.find('(')].strip()
    meta['symbol'] = h1[h1.find('(') + 1:h1.find(')')]
    title = soup.find('div', class_='title').text
    match = date_pattern.search(title)
    if match:
        m, d, y = match.groups()
        meta['month'] = int(m)
        meta['day'] = int(d)
        meta['year'] = int(y)
    match = quarter_pattern.search(title)
    if match:
        meta['quarter'] = match.group(0)
    qa = 0
    speaker_types = ['Executives', 'Analysts']
    for header in [p.parent for p in soup.find_all('strong')]:
        text = header.text.strip()
        if text.lower().startswith('copyright'):
            continue
        elif text.lower().startswith('question-and'):
            qa = 1
            continue
        elif any([type in text for type in speaker_types]):
            for participant in header.find_next_siblings('p'):
                if participant.find('strong'):
                    break
                else:
                    participants.append([text, participant.text])
        else:
            p = []
            for participant in header.find_next_siblings('p'):
                if participant.find('strong'):
                    break
                else:
                    p.append(participant.text)
            content.append([header.text, qa, '\n'.join(p)])
    return meta, participants, content 

我们将结果存储在几个.csv文件中,以便在我们使用 ML 处理第 14-16 章的自然语言时轻松访问:

def store_result(meta, participants, content):
    path = transcript_path / 'parsed' / meta['symbol']
    pd.DataFrame(content, columns=['speaker', 'q&a', 
              'content']).to_csv(path / 'content.csv', index=False)
    pd.DataFrame(participants, columns=['type', 'name']).to_csv(path / 
                 'participants.csv', index=False)
    pd.Series(meta).to_csv(path / 'earnings.csv') 

有关如何开发网络爬虫应用程序的详细信息和进一步资源的参考,请参阅 GitHub 存储库中的README

概要

在本章中,我们介绍了作为大数据革命结果而提供的新型替代数据来源,包括个人、业务流程和传感器,如卫星或 GPS 定位设备。我们提出了一个框架来从投资角度评估替代数据集,并列出了主要类别和提供者,帮助您浏览这个提供用于使用 ML 的算法交易策略的关键输入的庞大而迅速扩展的领域。

我们还探索了强大的 Python 工具,您可以使用这些工具以规模收集您自己的数据集。我们这样做是为了让您有可能通过网络爬虫作为算法交易员获得您自己的私人信息优势。

我们现在将在接下来的章节中进行 Alpha 因子的设计和评估,这些因子产生交易信号,并查看如何在组合环境中将它们结合起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值