机器学习算法交易教程第二版(二)
原文:
zh.annas-archive.org/md5/a3861c820dde5bc35d4f0200e43cd519译者:飞龙
第四章:金融特征工程-如何研究阿尔法因子
算法交易策略由指示何时买入或卖出资产以相对于基准(如指数)生成优越收益的信号驱动。资产回报中未被此基准暴露解释的部分称为阿尔法,因此旨在产生这种不相关回报的信号也称为阿尔法因子。
如果您已经熟悉 ML,您可能知道特征工程是成功预测的关键要素。在交易中也不例外。然而,投资领域特别丰富,有几十年的关于市场如何运作以及哪些特征可能比其他特征更好地解释或预测价格波动的研究。本章提供了一个概述作为您自己寻找阿尔法因子的起点。
本章还介绍了促进计算和测试阿尔法因子的关键工具。我们将重点介绍 NumPy、pandas 和 TA-Lib 库如何促进数据操作,并展示流行的平滑技术,如小波和卡尔曼滤波器,这有助于降低数据中的噪音。
我们还将预览如何使用交易模拟器 Zipline 评估(传统)阿尔法因子的预测性能。我们将讨论关键的阿尔法因子指标,如信息系数和因子换手率。随后将深入介绍使用机器学习进行回测交易策略的第六章,机器学习过程,其中涵盖了我们将在本书中用于评估交易策略的 ML4T 工作流程。
特别是,本章将讨论以下主题:
-
存在哪些类别的因子,为什么它们起作用以及如何衡量它们
-
使用 NumPy、pandas 和 TA-Lib 创建阿尔法因子
-
如何使用小波和卡尔曼滤波器去噪声数据
-
使用 Zipline 离线和在 Quantopian 上测试单个和多个阿尔法因子
-
如何使用 Alphalens 评估预测性能和换手率,包括信息系数(IC)等指标
您可以在 GitHub 仓库的相应目录中找到本章的代码示例和附加资源链接。笔记本包括图片的彩色版本。附录,阿尔法因子库,包含有关金融特征工程的其他信息,包括 100 多个示例,您可以借此为自己的策略提供帮助。
实践中的阿尔法因子-从数据到信号
阿尔法因子是旨在预测资产价格波动的原始数据转换。它们旨在捕捉驱动资产回报的风险。因子可能结合一个或多个输入,但每次策略评估因子以获得信号时都会为每个资产输出单个值。交易决策可能依赖于跨资产的相对因子值或单个资产的模式。
Alpha 因子的设计、评估和组合是算法交易策略工作流程研究阶段的关键步骤,如图 4.1所示:
图 4.1:Alpha 因子研究和执行工作流程
本章重点讨论研究阶段;下一章将涵盖执行阶段。本书的其余部分将重点介绍如何利用机器学习从数据中学习新因子,并有效地汇总来自多个 Alpha 因子的信号。
Alpha 因子是市场、基本和替代数据的转换,其中包含预测信号。一些因子描述了基本的、全球经济范围内的变量,如增长、通胀、波动率、生产率和人口风险。其他因子代表了投资风格,如价值或成长,以及可以交易的动量投资,因此市场定价了这些因子。还有一些因子解释了基于金融市场的经济学或制度设置,或投资者行为的价格变动,包括这种行为的已知偏见。
因子背后的经济理论可以是理性的,这样因子在长期内的回报可以弥补其在不景气时期的低回报。它也可以是行为的,因子风险溢价是由于可能偏向的、或不完全理性的代理行为而产生的,这些行为并不能被套利掉。
不断寻找和发现新的因子,可能更好地捕捉已知的或反映新的回报驱动因素。Research Affiliates 的联合创始人杰森·许(Jason Hsu)发现,截至 2015 年,已经有大约 250 种因子在权威期刊中发表了实证证据。他估计,每年这个数字可能增加 40 个因子。
为了避免错误的发现并确保因子提供一致的结果,它应该基于各种已建立的因子类别,如动量、价值、波动率或质量及其基本原理,具有有意义的经济直觉,我们将在下一节中概述。这使得因子更有可能反映市场愿意补偿的风险。
Alpha 因子是通过对原始市场、基本或替代数据进行简单算术转换而产生的,例如随时间变化的变量的绝对或相对变化,数据系列之间的比率,或在时间窗口上的聚合,如简单或指数移动平均值。它们还包括从价格和成交量模式的技术分析中产生的指标,例如需求与供应之间的相对强度指数,以及从证券基本面分析中熟悉的众多指标。Kakushadze(2016)列出了 101 种 Alpha 因子的公式,其中 80%在撰写本文时被 WorldQuant 对冲基金用于生产。
历史上,交易策略应用简单的排名启发式、价值阈值或分位数截断到投资领域中的一个或多个 alpha 因素。例如,华伦·巴菲特喜爱的书籍之一,格雷厄姆和多德(1934 年)的证券分析中普及的价值投资方法,依赖于诸如账面市值比这样的指标。
预测超过市场回报的 alpha 因素的现代研究由尤金·法玛(2013 年诺贝尔经济学奖获得者)和肯尼斯·弗伦奇领导,后者提供了关于规模和价值因素(1993 年)的证据。这项工作导致了三因子和五因子模型,我们将在第七章,线性模型 - 从风险因素到收益预测中使用作者在他们的网站上提供的因子收益的每日数据进行讨论。关于现代因子投资的卓越、更近期的概述由安德鲁·安格(2014 年)撰写,他在 BlackRock 领导这一学科,该公司管理着接近 7 万亿美元。
正如我们将在本书中看到的,ML 在从更多样化和更大规模的输入数据中直接提取信号方面已被证明相当有效,而不使用规定的公式。然而,正如我们将看到的,alpha 因素仍然是一个 ML 模型的有用输入,该模型以比手动设置规则更优的方式结合它们的信息内容。
因此,算法交易策略今天利用大量信号,其中许多信号在个体上可能很弱,但当与 ML 算法通过模型驱动或传统因素组合时,可以产生可靠的预测。
建立在数十年的因子研究基础上
在理想化的世界中,风险因素应该彼此独立,产生正的风险溢价,并形成一个完整的集合,涵盖资产在给定类别中的所有风险维度,并解释系统性风险。实际上,这些要求仅在近似情况下成立,并且不同因素之间存在重要的相关性。例如,动量在较小的公司中通常更强(侯,薛和张,2015 年)。我们将展示如何使用无监督学习派生合成的、数据驱动的风险因素—特别是主成分和独立成分分析—在第十三章,数据驱动风险因素和无监督学习的资产配置中。
在本节中,我们将回顾在金融研究和交易应用中突出的一些关键因素类别,解释它们的经济原理,并提供通常用于捕捉这些收益驱动因素的指标。
在接下来的部分中,我们将演示如何使用 NumPy 和 pandas 实现其中一些因素,使用 TA-Lib 库进行技术分析,并演示如何使用 Zipline 回测库评估因素。我们还将重点介绍一些内置于 Zipline 中的因素,在 Quantopian 平台上可用。
动量和情绪 - 趋势是你的朋友
动量投资是最为成熟的因子策略之一,自从 Jegadeesh 和 Titman(1993)在美国股票市场提供定量证据以来得到了支持。它遵循这句格言:趋势是你的朋友或让你的赢家持续奔跑。动量因子旨在做多表现良好的资产,同时做空一段时间内表现不佳的资产。最近,AQR 2000 亿美元对冲基金的创始人 Clifford Asness 展示了跨越八个不同资产类别和市场的动量效应证据(Asness、Moskowitz 和 Pedersen,2013)。
使用这一因子的策略的前提是资产价格存在趋势,表现为正的串行相关性。这种价格动量违背了有效市场假说,该假说认为过去的价格回报单独不能预测未来的表现。尽管存在相反的理论论点,但价格动量策略已经在各种资产类别中产生了正的回报,并且是许多交易策略的重要组成部分。
图 4.2中的图表显示了基于其暴露于各种阿尔法因子的投资组合的历史表现(使用来自 Fama-French 网站的数据)。赢家减输家(WML)因子代表了包含美国股票的投资组合在前 2-12 个月的收益中排名前三个和最后三个十分位数的差异:
图 4.2:各种风险因子的回报
动量因子在 2008 年危机之前明显优于其他显著的风险因子。其他因素包括高减低(HML)价值因子,强劲减弱劲(RMW)盈利能力因子和保守减激进(CMA)投资因子。股票溢价是市场回报(例如,标准普尔 500 指数)与无风险利率之间的差异。
为什么动量和情绪可能驱动超额回报?
动量效应的原因指向投资者行为、持续的供需失衡、风险资产与经济之间的正反馈循环或市场微观结构。
行为学理论反映了对市场新闻的低反应(Hong、Lim 和 Stein,2000)和过度反应(Barberis、Shleifer 和 Vishny,1998)的偏见,因为投资者以不同的速度处理新信息。在对新闻的初始低反应之后,投资者经常推断过去的行为并创造价格动量。科技股在 90 年代晚期市场泡沫期间的上涨就是一个极端的例子。恐惧和贪婪心理也促使投资者增加对获胜资产的暴露,并继续出售亏损资产(Jegadeesh 和 Titman,2011)。
动量也可以有基本驱动因素,比如风险资产和经济之间的正反馈循环。经济增长推动了股票,而由此产生的财富效应通过更高的支出再次反馈到经济中,进而促进了增长。价格和经济之间的正反馈往往会使股票和信贷的动量延续到比债券、FOEX 和大宗商品更长的时间范围,而在这些市场中,负反馈会引发逆转,需要更短的投资周期。动量的另一个原因可能是由于市场摩擦导致的持续的供需失衡。一个例子是商品生产在适应需求变化方面的延迟。石油生产可能滞后于经济蓬勃发展带来的更高需求多年,而持续的供应短缺可能会触发和支持价格上涨的动量(Novy-Marx,2015)。
在较短的,日内时间范围内,市场微观结构效应也可以产生价格动量,因为投资者实施模拟其偏好的策略。例如,根据交易智慧在资产表现不佳时割损和在表现良好时保留利润的原则,投资者使用交易策略,如止损、常比例组合保险(CPPI)、动态对冲和基于期权的策略,如保护性买入期权。这些策略会产生动量,因为它们意味着在资产表现不佳时就提前承诺卖出,在表现良好时就提前承诺买入。
类似地,风险平价策略(见下一章)倾向于购买通常表现良好的低波动资产,并出售通常表现不佳的高波动资产(请参阅本章后面的波动性和规模异常部分)。使用这些策略自动平衡投资组合会加强价格动量。
如何衡量动量和情绪
动量因子通常是通过识别趋势和模式来从价格时间序列中得出的。它们可以基于绝对收益或相对收益构建,通过比较资产横截面或分析资产的时间序列,在或跨传统资产类别和不同时间范围内进行。
下表列出了一些流行的说明性指标(请参阅附录以获取公式):
其他情绪指标包括以下指标;诸如分析师预测之类的输入可以从 Quandl 或 Bloomberg 等数据提供商获取:
| 因素 | 描述 |
|---|---|
| 盈利预测计数 | 该指标按照一致性预测的数量对股票进行排名,作为分析师覆盖和信息不确定性的代理。更高的值更可取。 |
| 推荐 N 个月的变化 | 该因素按照前N个月的一致性推荐变化对股票进行排名,改进是可取的(无论是从强烈卖出到卖出,还是从买入到强烈买入等等)。 |
| 股份在过去 12 个月的变化 | 该因素衡量了公司分割调整后的股票数量在过去 12 个月的变化,其中负变化意味着股票回购,并且是可取的,因为它表明管理层认为股票相对于其内在价值和未来价值而言是便宜的。 |
| 目标价格的 6 个月变化 | 该指标跟踪分析师目标价格的 6 个月变化。更高的正变化自然更可取。 |
| 净盈利修订 | 该因素表示盈利预测的上调和下调修订之间的差异,作为总修订数量的百分比。 |
| 短期利益占流通股的百分比 | 这个指标是当前被卖空的流通股的百分比,即投资者借来卖出的股票,并在以后的某一天回购它,同时猜测其价格将下跌。因此,高水平的卖空利益表明负面情绪,并且预计将来会信号较差的表现。 |
还有许多数据提供商致力于提供从社交媒体(例如 Twitter)构建的情绪指标。我们将在本书的第三部分中使用自然语言处理创建我们自己的情绪指标。
价值因素 - 寻找基本便宜货物
相对于其基本价值的低价格股票往往会提供超过市值加权基准的回报。价值因子反映了这种相关性,并旨在发送对相对便宜的被低估资产的买入信号以及对被高估资产的卖出信号。因此,任何价值策略的核心都是估算资产公平或基本价值的模型。公平价值可以被定义为绝对价格水平,与其他资产的价差,或者资产应该交易的范围。
相对价值策略
价值策略依赖价格回归到资产公平价值的均值。它们假设价格只是暂时偏离公平价值,这是由于行为效应,如过度反应或群体行为,或者流动性效应,例如临时市场影响或长期供需摩擦所致。价值因子通常表现出与动量因子相反的特性,因为它们依赖于均值回归。对于股票来说,价值股的相反是增长股,其高估值是由于增长预期所致。
价值因子可以启用广泛的系统化策略,包括基本和市场估值以及跨资产相对价值。它们经常被集体标记为统计套利(StatArb)策略,并作为市场中性的多空组合进行实施,没有暴露于其他传统或替代风险因子。
基本价值策略
基本价值策略从经济和基本指标中推导出公平资产价值,这些指标依赖于目标资产类别。在固定收益、货币和商品中,指标包括资本账户余额的水平和变化、经济活动、通货膨胀或资金流动。对于股票和公司信用,价值因子可以追溯到格雷厄姆和多德之前提到的证券分析。股权价值方法将股票价格与基本指标进行比较,如账面价值、销售额、利润或各种现金流指标。
市场价值策略
市场价值策略使用统计或机器学习模型来识别由于流动性提供效率低下而导致的定价错误。统计套利和指数套利是突出的例子,它们捕捉了短期时间段内临时市场影响的回归。(我们将在第九章,用于波动率预测和统计套利的时间序列模型中介绍配对交易)。在较长的时间范围内,市场价值交易还利用了股票和商品的季节性效应。
跨资产相对价值策略
跨资产相对价值策略关注跨资产类别的错定价。例如,可转债套利涉及在债券可以转换为股票的情况下进行交易,以及单个公司的基础股票之间的相对价值。相对价值策略还包括信用和股票波动率之间的交易,利用信用信号交易股票或商品和相关股票之间的交易。
为什么价值因素有助于预测回报?
存在价值效应的合理和行为解释都有,定义为价值股票组合相对于成长股票组合的超额回报,前者市值较低,后者市值相对于基本面较高。我们将从众多研究中引用一些著名的例子(例如,看看法马和弗伦奇,1998 年,以及阿斯尼斯,莫斯科维茨和佩德森,2013 年)。
在理性、有效市场观点中,价值溢价是对更高的实际或感知风险的补偿。研究人员提出了证据表明,价值公司比更精简、更灵活的成长公司更难适应不利的经济环境,或者价值股风险与高财务杠杆和更不确定的未来收益有关。价值和小盘股组合也被证明对宏观冲击更敏感,而成长和大盘股组合则较少(拉克尼绍克,施莱费尔和维希尼,1994 年)。
从行为学角度来看,价值溢价可以通过损失规避和心理会计偏差来解释。由于之前的收益提供了保护垫,投资者可能对具有强劲近期表现的资产的损失不那么担心。这种损失规避偏差导致投资者认为股票比以前更不具有风险,并以较低的速度贴现其未来现金流。相反,近期表现不佳可能会导致投资者提高资产的贴现率。
这些不同的回报期望可以产生价值溢价:相对于基本面的高价倍数的成长股票过去表现良好,但由于投资者对较低风险的偏见感知,他们将需要更低的平均回报率,而对于价值股票则相反。
如何捕捉价值效应
许多估值代理是从基本数据计算出来的。这些因素可以组合为机器学习估值模型的输入,以预测资产价格。以下示例适用于股票,我们将在接下来的章节中看到一些这些因素是如何使用的:
| 因素 | 描述 |
|---|---|
| 现金流量收益率 | 该比率将每股经营现金流量除以股价。较高的比率意味着股东获得更好的现金回报(如果使用股息或股票回购或将利润有利地再投资于业务中)。 |
| 自由现金流收益率 | 此比率将每股自由现金流除以股价,自由现金流反映了经过必要支出和投资后可用于分配的现金金额。较高且增长的自由现金流收益率通常被视为超额表现的信号。 |
| 投资资本现金流回报率(CFROIC) | CFROIC 衡量了公司的现金流盈利能力。它将经营现金流除以投入资本,定义为总债务加上净资产。更高的回报意味着公司在给定的投入资本量下拥有更多现金,为股东创造更多价值。 |
| 现金流与总资产比 | 此比率将经营现金流除以总资产,表示公司相对于其资产能产生多少现金,较高的比率是更好的,就像 CFROIC 一样。 |
| 企业自由现金流与企业价值比 | 此比率衡量了公司相对于其企业价值(即股权和债务的综合价值)所产生的自由现金流,债务和股权价值可从资产负债表中获取,但市场价值通常提供了更准确的图景,假设相应资产是活跃交易的。 |
| EBITDA 与企业价值比 | 此比率衡量了公司的利息、税收、折旧和摊销前收入(EBITDA),这是相对于其企业价值的现金流的替代指标。 |
| 收益率 | 此比率将过去 12 个月的收益总和除以最后的市场(收盘)价格。 |
| 前瞻收益率 | 这个比率不使用历史收益,而是将股票分析师预测的下一个 12 个月的平均收益除以最后的价格。 |
| PEG 比率 | 市盈率增长比(PEG)比率将一家公司的市盈率(P/E)比率除以一个给定时期的盈利增长率。该比率通过公司的盈利增长调整支付的每一美元的价格(由 P/E 比率衡量)。 |
| 前 1 年相对于行业的 P/E | 相对于相应行业 P/E 的 P/E 比率预测。它旨在通过考虑估值中的行业差异来减轻通用 P/E 比率的行业偏见。 |
| 销售收益率 | 此比率衡量了股票相对于其产生收入能力的估值。其他条件相等的情况下,具有较高历史销售价格比的股票预计将表现优异。 |
| 前瞻销售收益率 | 前瞻销售收益率使用分析师的销售预测,结合成一个(加权)平均值。 |
| 账面价值收益率 | 此比率将历史账面价值除以股价。 |
| 股息收益率 | 当前年化股息除以最后的收盘价格。贴现现金流估值假设公司的市场价值等于其未来现金流的现值。 |
第二章,市场和基本数据-来源和技术,讨论了您如何从公司文件中获取用于计算这些指标的基本数据。
波动性和规模异常
规模效应是较旧的风险因素之一,与市值较低的股票的超额表现有关(参见本节开头的图 4.2)。最近,已经显示低波动性因子可以捕捉到波动性、β值或特异风险低于平均水平的股票的超额回报。市值较大的股票 tend to 有较低的波动性,因此传统的规模因子经常与最近的波动性因子结合使用。
低波动性异常是与金融基本原理相抵触的实证难题。资本资产定价模型(CAPM)和其他资产定价模型断言,较高的风险应该获得较高的回报(我们将在下一章中详细讨论),但在许多市场和较长时期内,情况恰恰相反,较低风险的资产表现优于其较高风险的同行。
图 4.3 绘制了 1990-2019 年标普 500 收益率的滚动平均值与 VIX 指数的关系,VIX 指数衡量标普 100 名义期权的隐含波动率。它说明了股票收益率和这个波动率衡量指标如何以负相关的方式随着时间变化。除了这种总体效应之外,还有证据表明,对 VIX 变化更敏感的股票表现更差(Ang 等人,2006 年):
图 4.3:VIX 与标普 500 之间的相关性
为什么波动性和规模能预测回报?
低波动性异常与有效市场假设和 CAPM 假设相矛盾。已提出了几种行为解释来解释其存在。
彩票效应建立在实证证据之上,即个人承担类似于彩票的赌注,预期损失很小但潜在赢利很大,即使这种大赢利可能性相当低。如果投资者认为低价、波动性大的股票的风险收益概况类似于一张彩票,那么它可能是一个有吸引力的赌注。因此,由于其偏好的原因,投资者可能会为高波动性股票付出过高的价格,并为低波动性股票付出过低的价格。
代表性偏见表明,投资者将少数广为人知的高波动性股票的成功推广到所有高波动性股票,而忽视了这些股票的投机性质。
投资者可能也会对自己预测未来的能力过于自信,对于波动较大、结果更不确定的股票,他们的意见分歧更大。由于通过看多——即持有资产——来表达积极观点比通过看空来表达消极观点更容易,乐观主义者可能会超过悲观主义者,并继续推高波动性股票的价格,导致回报降低。
此外,投资者在牛市和危机期间的行为不同。在牛市中,贝塔的离散度要低得多,因此低波动性股票的表现不会很差,如果有的话,在危机期间,投资者会寻找或保留低波动性股票,贝塔离散度会增加。因此,长期来看,波动性较低的资产和投资组合表现更好。
如何衡量波动性和规模
用于识别低波动性股票的指标涵盖了广泛的范围,从实现波动率(标准偏差)到预测(隐含)波动率和相关性。一些将低波动性的运作定义为低贝塔。有关波动性异常的证据在不同的指标下似乎都很坚实(Ang,2014)。
量化投资的质量因素
质量因素旨在捕捉公司获得的超额回报,这些公司非常盈利、运营高效、安全、稳定且治理良好——简而言之,质量高。市场似乎也奖励相对盈利的确定性,并惩罚盈利波动性高的股票。
对高质量企业进行组合倾向在依赖基本分析的股票挑选者中长期被提倡,但在量化投资中却是一个相对较新的现象。主要挑战是如何使用定量指标一致和客观地定义质量因素,考虑到质量的主观性。
基于独立质量因素的策略往往呈逆周期性,因为投资者为了最小化下行风险支付溢价并推高估值。因此,质量因素经常与其他风险因素结合在多因子策略中,最常见的是与价值因素结合以制定合理价位的质量策略。
长短期质量因素往往具有负的市场贝塔,因为它们看多质量高、波动性低的股票,并做空波动性更高、质量更低的股票。因此,质量因素通常与低波动性和动量因素呈正相关,与价值和广泛市场敞口呈负相关。
为什么质量很重要
质量因素可能暗示超额收益,因为优秀的基本面,如持续的盈利能力、现金流量稳定增长、谨慎的杠杆化、对资本市场融资需求较低,或者低金融风险支撑了对股票的需求,从而长期支持这些公司的股价。从公司财务的角度来看,质量好的公司通常会谨慎管理资本,降低过度杠杆化或过度资本化的风险。
行为解释指出,投资者对于质量的信息反应不足,类似于动量理论,投资者追逐赢家并卖出失败者。
质量溢价的另一个论点是一个类似于成长股的赶羊效应。基金经理可能会发现更容易去购买一个基本面强劲的公司,即使它变得昂贵,而不是一个更加波动(风险)的价值股。
如何衡量资产质量
质量因素依赖于从资产负债表和利润表计算的指标,这些指标反映在高利润或现金流量利润率、运营效率、财务实力和竞争力方面的盈利能力,因为它意味着能够长期维持盈利地位的能力。
因此,质量已经使用毛利率(最近已添加到法玛-法国因子模型中;见第七章,线性模型-从风险因子到收益预测)、投入资本回报率、收益波动率低、或者各种盈利能力、收益质量和杠杆指标的组合进行衡量,以下表格列出了一些选项。
收益管理主要通过操纵应计项目进行。因此,应计项目的规模通常被用作收益质量的代理:相对于资产的较高的总应计项目使得低收益质量更有可能。然而,这并不明确,因为应计项目既可以反映出收益操纵,也可以反映出未来业务增长的会计估计:
| 因素 | 描述 |
|---|---|
| 资产周转率 | 这个因素衡量公司如何高效地利用其需要资本的资产产生收入,并通过销售额除以总资产来计算。较高的周转率更好。 |
| 资产周转率 12 个月变动 | 这个因素衡量了管理团队在过去一年内利用资产产生收入的效率变化。通常预期效率改善水平最高的股票会表现出色。 |
| 流动比率 | 流动比率是一种衡量公司偿付短期债务能力的流动性指标。它将公司的流动资产与流动负债进行比较,从质量的角度来看,较高的流动比率更好。 |
| 利息覆盖率 | 这个因素衡量公司支付债务利息的容易程度。它是通过将公司的利息前税收盈利(EBIT)除以其利息支出来计算的。较高的比率是可取的。 |
| 杠杆 | 具有比股本更多债务的公司被认为是高度杠杆的。债务股本比通常与前景呈反比关系,较低的杠杆比较好。 |
| 股息支付比率 | 将利润支付给股东的股息所占比例。股息支付比率较高的股票排名较高。 |
| 股东权益回报率(ROE) | ROE 被计算为净收入与股东权益的比率。具有较高历史股东权益回报率的股票排名较高。 |
配备了与不同程度的异常收益相关的阿尔法因子的高级分类,我们现在将开始从市场、基本和替代数据中开发我们自己的金融特征。
工程化预测回报的阿尔法因子
基于对关键因子类别、其基本原理和流行度量的概念理解,一个关键任务是识别可能更好地捕捉到前述回报驱动因素所体现的风险的新因子,或者找到新因子。在任一情况下,将重要的是将创新因子的表现与已知因子的表现进行比较,以识别增量信号增益。
促进数据转化为因子的关键工具包括用于数值计算的 Python 库 NumPy 和 pandas,以及用于技术分析的专用库 TA-Lib 的 Python 包装器。另一种选择是 Zura Kakushadze 在 2016 年论文《101 Formulaic Alphas》中开发的表达式阿尔法,并由 alphatools 库实现。此外,Quantopian 平台提供了大量内置因子,以加速研究过程。
要将一个或多个因子应用于投资范围,我们可以使用 Zipline 回测库(其中还包括一些内置因子),并使用 Alphalens 库评估它们的性能,使用下一节讨论的指标。
如何使用 pandas 和 NumPy 工程化因子
NumPy 和 pandas 是定制因子计算的关键工具。本节演示了如何使用它们快速计算产生各种阿尔法因子的转换。如果您对这些库不熟悉,特别是我们将在本书中始终使用的 pandas,请参阅 GitHub 存储库中本章的README,其中包含指向文档和教程的链接。
alpha_factors_in_practice目录中的笔记本feature_engineering.ipynb包含了创建各种因子的示例。该笔记本使用了根目录 GitHub 存储库中data文件夹中create_data.ipynb笔记本生成的数据,该数据以 HDF5 格式存储以加快访问速度。有关 pandas DataFrames 的 parquet、HDF5 和 CSV 存储格式的比较,请参阅 GitHub 存储库中第二章目录中的笔记本storage_benchmarks.ipynb。
NumPy 科学计算库是由 Travis Oliphant 于 2005 年创建的,通过整合自上世纪 90 年代中期以来开发的较旧的 Numeric 和 Numarray 库而形成。它采用高性能的n维数组数据结构ndarray,其功能与 MATLAB 相当。
pandas 库于 2008 年出现,当时 Wes McKinney 在 AQR Capital Management 工作。它提供了 DataFrame 数据结构,该结构基于 NumPy 的ndarray,但允许更友好的数据操作和基于标签的索引。它包括丰富的计算工具,特别适用于金融数据,包括具有自动日期对齐的丰富时间序列操作,我们将在这里探讨。
以下各节演示了将原始股票价格数据转换为选定因子的一些步骤。有关我们在此处省略以节省空间的其他详细信息和可视化,请参见笔记本feature_engineering.ipynb。有关如何使用 pandas 和 NumPy 的文档和教程的链接,请参见 GitHub 上本章的README中列出的资源。
加载、切片和重塑数据
在加载 Quandl Wiki 美国股票价格数据之后,我们通过将pd.IndexSlice应用于包含时间戳和股票代码信息的pd.MultiIndex来选择 2000-18 年的时间切片。然后我们使用.stack()方法选择并取消旋转调整后的收盘价列,以将 DataFrame 转换为宽格式,其中股票代码在列中,时间戳在行中:
idx = pd.IndexSlice
with pd.HDFStore('../../data/assets.h5') as store:
prices = (store['quandl/wiki/prices']
.loc[idx['2000':'2018', :], 'adj_close']
.unstack('ticker'))
prices.info()
DatetimeIndex: 4706 entries, 2000-01-03 to 2018-03-27
Columns: 3199 entries, A to ZUMZ
重采样 - 从日频到月频
为了减少训练时间并尝试更长期限的策略,我们使用可用的调整后收盘价将业务日数据转换为月末频率:
monthly_prices = prices.resample('M').last()
如何计算多个历史期间的收益
为了捕捉时间序列动态如动量模式,我们使用pct_change(n_periods)方法计算历史多期收益,其中n_periods表示滞后期数。然后我们使用.stack()将宽格式结果转换回长格式,使用.pipe()将.clip()方法应用于结果 DataFrame,并在[1%, 99%]水平上进行调整收益;也就是说,我们在这些百分位数上限制异常值。
最后,我们使用几何平均值对收益进行标准化。在使用.swaplevel()更改MultiIndex级别顺序后,我们获得了六个不同期间的复合月收益,范围从 1 到 12 个月:
outlier_cutoff = 0.01
data = pd.DataFrame()
lags = [1, 2, 3, 6, 9, 12]
for lag in lags:
data[f'return_{lag}m'] = (monthly_prices
.pct_change(lag)
.stack()
.pipe(lambda x:
x.clip(lower=x.quantile(outlier_cutoff),
upper=x.quantile(1-outlier_cutoff)))
.add(1)
.pow(1/lag)
.sub(1)
)
data = data.swaplevel().dropna()
data.info()
MultiIndex: 521806 entries, (A, 2001-01-31 00:00:00) to (ZUMZ, 2018-03-
31 00:00:00)
Data columns (total 6 columns):
return_1m 521806 non-null float64
return_2m 521806 non-null float64
return_3m 521806 non-null float64
return_6m 521806 non-null float64
return_9m 521806 non-null float64
return_12m 521806 non-null float6
我们可以使用这些结果来计算基于长期收益与最近一个月收益之间差异的动量因子,以及 3 个月和 12 个月收益之间的差异,如下所示:
for lag in [2,3,6,9,12]:
data[f'momentum_{lag}'] = data[f'return_{lag}m'].sub(data.return_1m)
data[f'momentum_3_12'] = data[f'return_12m'].sub(data.return_3m)
使用滞后收益和不同持有期
为了将滞后值用作与当前观察值相关的输入变量或特征,我们使用.shift()方法将历史收益移至当前期间:
for t in range(1, 7):
data[f'return_1m_t-{t}'] = data.groupby(level='ticker').return_1m.shift(t)
类似地,为了计算不同持有期的回报,我们使用之前计算的标准化周期回报,并将它们向后移以与当前的金融特征对齐:
for t in [1,2,3,6,12]:
data[f'target_{t}m'] = (data.groupby(level='ticker')
[f'return_{t}m'].shift(-t))
该笔记本还演示了如何计算不同回报系列的各种描述统计量,并使用 seaborn 库可视化它们的相关性。
计算因子贝塔
我们将在第七章中介绍 Fama-French 数据,使用线性回归来估计资产对常见风险因子的暴露。五个 Fama-French 因子,即市场风险、规模、价值、经营盈利能力和投资,在经验上被证明可以解释资产回报。它们通常用于评估投资组合对风险和回报的著名驱动因素的暴露,然后将未解释的部分归因于管理者的特殊技能。因此,在旨在预测未来回报的模型中,将过去的因子暴露作为金融特征是很自然的。
我们可以使用 pandas-datareader 访问历史因子回报,并使用 pyfinance 库中的 PandasRollingOLS 滚动线性回归功能来估计历史暴露,具体如下:
factors = ['Mkt-RF', 'SMB', 'HML', 'RMW', 'CMA']
factor_data = web.DataReader('F-F_Research_Data_5_Factors_2x3',
'famafrench', start='2000')[0].drop('RF', axis=1)
factor_data.index = factor_data.index.to_timestamp()
factor_data = factor_data.resample('M').last().div(100)
factor_data.index.name = 'date'
factor_data = factor_data.join(data['return_1m']).sort_index()
T = 24
betas = (factor_data
.groupby(level='ticker', group_keys=False)
.apply(lambda x: PandasRollingOLS(window=min(T, x.shape[0]-1), y=x.return_1m, x=x.drop('return_1m', axis=1)).beta))
如前所述,我们将更详细地探讨 Fama-French 因子模型和线性回归,在第七章中,更多地从风险因子到回报预测的线性模型。查看笔记本 feature_engineering.ipynb 以获取其他示例,包括滞后和前瞻回报的计算。
如何添加动量因子
我们可以使用 1 个月和 3 个月的结果来计算简单的动量因子。下面的代码示例展示了如何计算长期回报与最近一个月的回报之间的差异,以及 3 个月与 12 个月回报之间的差异:
for lag in [2,3,6,9,12]:
data[f'momentum_{lag}'] = data[f'return_{lag}m'].sub(data.return_1m)
data[f'momentum_3_12'] = data[f'return_12m'].sub(data.return_3m)
添加时间指标以捕捉季节效应
基本因子还包括季节性异常,如一月效应,已经观察到这个月份的股票回报率较高,可能是出于税收原因。这和其他季节性效应可以通过代表特定时间段的指示变量来建模,例如年份和/或月份。这些可以按以下方式生成:
dates = data.index.get_level_values('date')
data['year'] = dates.year
data['month'] = dates.month
如何创建滞后的回报特征
如果你想使用滞后的回报,也就是来自以前期间的回报作为输入变量或特征来训练一个学习回报模式以预测未来回报的模型,你可以使用 .shift() 方法将历史回报移动到当前期间。下面的例子将过去 1 到 6 个月的回报移动到相应的滞后,使它们与当前月份的观察相关联:
for t in range(1, 7):
data[f'return_1m_t-{t}'] = data.groupby(level='ticker').return_1m.shift(t)
如何创建前瞻回报
同样地,你可以使用 .shift() 来为当前期间创建前瞻回报,也就是将会发生在未来的回报,只需使用负周期(假设你的数据按升序排序):
for t in [1,2,3,6,12]:
data[f'target_{t}m'] = (data.groupby(level='ticker')
[f'return_{t}m'].shift(-t))
我们将在第六章,机器学习过程开始训练 ML 模型时使用前向回报。
如何使用 TA-Lib 创建技术阿尔法因子
TA-Lib 是一个使用 C++编写的开源库,并带有 Python 接口,被广泛用于交易软件开发。它包含 200 多个流行技术分析指标的标准化实现;也就是说,这些指标只使用市场数据,即价格和成交量信息。
TA-Lib 与 pandas 和 NumPy 兼容,其使用非常简单。以下示例演示了如何计算两个流行指标。
布林带由简单移动平均线(SMA)围绕着两个标准差上下的带组成。当价格在两条带之外的上下方时,这种带可以用于可视化潜在的超买/超卖条件,分别是。发明者约翰·波林格实际上推荐了一套包含 22 条规则的交易系统,用于生成交易信号。
我们可以计算布林带,并且,为了比较,在本章节中先前描述的相对强度指数如下所示。
我们加载单只股票的调整收盘价—在本例中为 AAPL:
with pd.HDFStore(DATA_STORE) as store:
data = (store['quandl/wiki/prices']
.loc[idx['2007':'2010', 'AAPL'],
['adj_open', 'adj_high', 'adj_low', 'adj_close',
'adj_volume']]
.unstack('ticker')
.swaplevel(axis=1)
.loc[:, 'AAPL']
.rename(columns=lambda x: x.replace('adj_', '')))
然后,我们通过相关的 TA-Lib 函数将一维pd.Series传递:
from talib import RSI, BBANDS
up, mid, low = BBANDS(data.close, timeperiod=21, nbdevup=2, nbdevdn=2,
matype=0)
rsi = RSI(adj_close, timeperiod=14)
然后,我们在 DataFrame 中收集结果,并使用 AAPL 股票价格和 RSI 与 30/70 线绘制布林带,这表明有长期/短期机会:
data = pd.DataFrame({'AAPL': data.close, 'BB Up': up, 'BB Mid': mid,
'BB down': low, 'RSI': rsi})
fig, axes= plt.subplots(nrows=2, figsize=(15, 8))
data.drop('RSI', axis=1).plot(ax=axes[0], lw=1, title='Bollinger Bands')
data['RSI'].plot(ax=axes[1], lw=1, title='Relative Strength Index')
axes[1].axhline(70, lw=1, ls='--', c='k')
axes[1].axhline(30, lw=1, ls='--', c='k')
结果显示在图 4.4中,相当混合—在早期危机后复苏期间,两个指标都表明超买条件,当价格继续上涨时:
图 4.4:布林带和相对强度指数
使用卡尔曼滤波器对阿尔法因子进行去噪
数据中的噪声概念与信号处理领域相关,旨在从发送的信号中检索正确信息,例如,通过电磁波形式通过空气发送。随着波浪穿过空间,环境干扰可以以噪声的形式添加到原始纯净信号中,这使得在接收后需要分离这两个信号。
卡尔曼滤波器于 1960 年引入,并已成为许多需要处理嘈杂数据的应用程序非常流行,因为它允许更准确地估计底层信号。
此技术广泛用于跟踪计算机视觉中的对象,支持飞机和航天器的定位和导航,并基于嘈杂的传感器数据控制机器人运动,除了其在时间序列分析中的用途。
噪声在数据科学、金融和其他领域中也类似使用,这意味着原始数据包含有用信息,例如,以交易信号的形式,需要从不相关的、无关的信息中提取和分离出来。显然,我们不知道真实信号的事实有时会使这种分离变得相当具有挑战性。
我们将首先回顾卡尔曼滤波器的工作原理以及它做出的假设,然后我们将演示如何使用pykalman库将其应用于金融数据。
卡尔曼滤波器是如何工作的?
卡尔曼滤波器是一种动态线性模型,用于适应顺序数据,如时间序列,随着新信息的到来而适应。与使用固定大小窗口(如移动平均值)或给定一组权重(如指数移动平均值)不同,它根据概率模型将新数据合并到其对时间序列当前值的估计中。
更具体地说,卡尔曼滤波器是一种关于一系列观测z[1],z[2],…,z[T]和相应隐藏状态x[1],x[2],…,x[T]的概率模型(使用我们将在此处演示的pykalman库使用的符号)。这可以通过以下图表示:
图 4.5:卡尔曼滤波器作为图形模型
从技术上讲,卡尔曼滤波器采用贝叶斯方法,随着时间的推移传播状态变量x的后验分布,给定它们的测量z(有关贝叶斯推断的更多详细信息,请参见第十章,贝叶斯 ML - 动态夏普比率和配对交易)。我们还可以将其视为一种无监督算法,用于跟踪连续状态空间中的单个对象,在这里,我们将对象视为,例如,安全性的价值或回报,或一个 alpha 因子(有关第十三章,使用无监督学习的数据驱动风险因子和资产配置)。
为了从可能实时可用的一系列观测中恢复隐藏状态,该算法在两个步骤之间迭代:
-
预测步骤:估计过程的当前状态。
-
测量步骤:使用嘈杂的观测来通过以更加确定的估计更高的权重平均两个步骤的信息来更新其估计。
该算法背后的基本思想是:对动态系统和相应测量历史做出一些假设将使我们能够估计系统的状态,以一种最大化以前测量概率的方式。
实现其恢复隐藏状态的目标,卡尔曼滤波器做出以下假设:
-
我们正在建模的系统以线性方式行为。
-
隐藏状态过程是一个马尔可夫链,因此当前隐藏状态x[t]仅取决于最近的先前隐藏状态x[t-1]。
-
测量受高斯、不相关噪声的影响,具有恒定的协方差。
结果上,卡尔曼滤波器类似于隐藏的马尔可夫模型,只是潜变量的状态空间是连续的,并且隐藏和观测变量都具有正态分布,表示为 具有均值
和标准差
在数学术语中,该模型的关键组成部分(以及在 pykalman 实现中对应的参数)是:
-
初始隐藏状态具有正态分布:
具有
initial_state_mean,和
initial_state_covariance,。
-
隐藏状态 x[t+1] 是 x[t] 的仿射变换,其中
transition_matrixA,transition_offsetb,并且添加了带有transition_covarianceQ 的高斯噪声:。
-
观察 z[t] 是隐藏状态 x[t] 的仿射变换,其中
observation_matrixC,observation_offsetd,并且添加了带有observation_covarianceR 的高斯噪声:。
卡尔曼滤波器的优点之一是,它能够灵活适应具有不断变化分布特征的非平稳数据(有关平稳性的更多细节,请参见第九章,用于波动率预测和统计套利的时间序列模型)。
主要缺点是线性假设和高斯噪声的假设,而金融数据通常违反这些假设。为了解决这些缺点,卡尔曼滤波器已经扩展到具有非线性动态的系统,形式为扩展卡尔曼滤波器和无迹卡尔曼滤波器。粒子滤波器是一种使用基于采样的蒙特卡洛方法来估计非正态分布的替代方法。
如何使用 pykalman 应用卡尔曼滤波器
卡尔曼滤波器特别适用于随时间变化的数据值或模型参数的滚动估计。这是因为它根据新观察在每个时间步骤调整其估计,并且倾向于更加重视最近的观察。
除了传统的移动平均外,卡尔曼滤波器不要求我们指定用于估计的窗口长度。相反,我们从潜在状态的均值和协方差的估计开始,并让卡尔曼滤波器根据周期性观察来纠正我们的估计。本节的代码示例在笔记本 kalman_filter_and_wavelets.ipynb 中。
以下代码示例显示了如何将卡尔曼滤波器应用于平滑 2008-09 年期间的标准普尔 500 股价系列:
with pd.HDFStore(DATA_STORE) as store:
sp500 = store['sp500/stooq'].loc['2008': '2009', 'close']
我们使用单位协方差矩阵和零均值初始化 KalmanFilter(有关如何处理选择适当初始值的挑战的建议,请参阅 pykalman 文档):
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)
然后,我们运行 filter 方法来触发前向算法,该算法迭代地估计隐藏状态,即时间序列的均值:
state_means, _ = kf.filter(sp500)
最后,我们添加移动平均以进行比较并绘制结果:
sp500_smoothed = sp500.to_frame('close')
sp500_smoothed['Kalman Filter'] = state_means
for months in [1, 2, 3]:
sp500_smoothed[f'MA ({months}m)'] = (sp500.rolling(window=months * 21)
.mean())
ax = sp500_smoothed.plot(title='Kalman Filter vs Moving Average',
figsize=(14, 6), lw=1, rot=0)
图 4.6中的结果图表显示,卡尔曼滤波器的表现与 1 个月移动平均值相似,但对时间序列行为的变化更敏感:
图 4.6:卡尔曼滤波器与移动平均值
如何使用小波预处理嘈杂的信号
小波与傅立叶分析相关联,后者将不同频率的正弦和余弦波组合起来以逼近嘈杂的信号。虽然傅立叶分析特别适用于将信号从时间域转换为频率域,但小波可用于过滤可能出现在不同尺度上的特定模式,这反过来可能对应于一定的频率范围。
小波是将离散或连续时间信号分解为不同尺度分量的函数或类似波形。小波变换则利用小波作为缩放和平移的有限长度波形的扩展和平移副本来表示函数。对于具有不连续性和尖峰的函数以及近似非周期性或非平稳信号,该变换优于傅立叶变换。
要去噪信号,可以使用小波收缩和阈值方法。首先,选择特定的小波模式来分解数据集。小波变换产生与数据集中的细节相对应的系数。
阈值法的理念很简单,就是省略所有低于特定截止值的系数,假设它们代表不必要表示真实信号的细节。然后,这些剩余系数被用于反向小波变换以重构(去噪)数据集。
现在我们将使用 pywavelets 库将小波应用于嘈杂的股票数据。以下代码示例说明了如何使用 Daubechies 6 小波和不同阈值值进行前向和反向小波变换去噪标普 500 指数收益率。
首先,我们为 2008-09 期间生成每日标普 500 指数收益率:
signal = (pd.read_hdf(DATA_STORE, 'sp500/stooq')
.loc['2008': '2009']
.close.pct_change()
.dropna())
然后,我们从众多内置小波函数中选择了一个 Daubechies 小波:
import pywt
pywt.families(short=False)
['Haar', 'Daubechies', 'Symlets', 'Coiflets', 'Biorthogonal', 'Reverse biorthogonal', 'Discrete Meyer (FIR Approximation)', 'Gaussian', 'Mexican hat wavelet', 'Morlet wavelet', 'Complex Gaussian wavelets', 'Shannon wavelets', 'Frequency B-Spline wavelets', 'Complex Morlet wavelets']
Daubechies 6 小波由缩放函数 和小波函数
定义(有关详细信息,请参阅 PyWavelet 文档和附带的笔记本
kalman_filter_and_wavelets.ipynb中的所有内置小波函数的图表):
图 4.7:Daubechies 小波
给定小波函数,我们首先使用.wavedec函数分解返回信号,该函数产生小波变换的系数。接下来,我们过滤掉所有高于给定阈值的系数,然后使用仅这些系数的逆变换.waverec来重构信号:
wavelet = "db6"
for i, scale in enumerate([.1, .5]):
coefficients = pywt.wavedec(signal, wavelet, mode='per')
coefficients[1:] = [pywt.threshold(i, value=scale*signal.max(), mode='soft') for i in coefficients[1:]]
reconstructed_signal = pywt.waverec(coefficients, wavelet, mode='per')
signal.plot(color="b", alpha=0.5, label='original signal', lw=2,
title=f'Threshold Scale: {scale:.1f}', ax=axes[i])
pd.Series(reconstructed_signal, index=signal.index).plot(c='k', label='DWT smoothing}', linewidth=1, ax=axes[i])
笔记本展示了如何使用不同的阈值应用这种去噪技术,所得到的图表,在图 4.8中清楚地显示出更高的阈值值产生了显著更平滑的系列:
图 4.8:不同阈值的小波去噪
从信号到交易 - Zipline 用于回测
开源库 Zipline 是一个事件驱动的回测系统。它生成市场事件来模拟算法交易策略的反应,并跟踪其表现。一个特别重要的特性是,它为算法提供了避免前瞻性偏差的历史时点数据。
该库由众包的量化投资基金 Quantopian 推广,Quantopian 在生产中使用它来促进算法开发和实时交易。
在本节中,我们将简要演示其基本功能。第八章,ML4T 工作流程 - 从模型到策略回测,包含了更详细的介绍,以准备我们进行更复杂的用例。
如何回测单因子策略
您可以与数据包一起离线使用 Zipline 进行 alpha 因子的研究和评估。在 Quantopian 平台上使用时,您将获得更广泛的基本和替代数据。我们还将在本章中演示 Quantopian 研究环境,并在下一章中演示回测 IDE。本节的代码位于本章的 GitHub 存储库文件夹的01_factor_research_evaluation子目录中,其中包括安装说明和适用于 Zipline 依赖项的环境。
安装请参阅本章的 GitHub 上的README中的说明。安装后,在执行第一个算法之前,您需要摄取一个数据包,默认情况下包含 Quandl 社区维护的 3000 家美国上市公司的股价、股息和拆股数据。
运行以下代码需要 Quandl API 密钥,该代码将数据存储在您的home文件夹下的~/.zipline/data/<bundle>目录中:
$ QUANDL_API_KEY=<yourkey> zipline ingest [-b <bundle>]
市场数据的单个 alpha 因子
我们首先将在离线环境中说明 Zipline alpha 因子研究工作流程。特别是,我们将开发和测试一个简单的均值回归因子,该因子衡量了最近表现与历史平均值的偏离程度。
短期反转是一种常见策略,利用了股票价格很可能在不到 1 分钟到 1 个月的时间内恢复到滚动平均值的弱预测模式。详情请参阅笔记本single_factor_zipline.ipynb。
为此,该因子计算了最后一个月回报相对于过去一年内滚动月回报的z值。此时,我们不会下任何订单,只是为了说明CustomFactor的实现并在模拟过程中记录结果。
Zipline 包含许多内置因子,用于许多常见操作(有关详细信息,请参阅 GitHub 上链接的 Quantopian 文档)。虽然这通常很方便且足够,但在其他情况下,我们希望以不同方式转换我们的可用数据。为此,Zipline 提供了 CustomFactor 类,它为我们指定了一系列计算提供了很大的灵活性。它使用 NumPy 提供的各种功能来为证券的横截面和自定义回顾期提供各种计算。
为此,在进行一些基本设置之后,MeanReversion 以 CustomFactor 为子类,并定义了一个 compute() 方法。它创建了默认输入的月度回报率,覆盖了默认的一年窗口,以便 monthly_return 变量在给定日期的 Quandl 数据集中每个证券都有 252 行和一列。
compute_factors() 方法创建了一个 MeanReversion 因子实例,并创建了长、短和排名管道列。前两者包含布尔值,可用于下达订单,后者反映了用于评估整体因子性能的整体排名。此外,它使用内置的 AverageDollarVolume 因子将计算限制在更流动的股票上:
from zipline.api import attach_pipeline, pipeline_output, record
from zipline.pipeline import Pipeline, CustomFactor
from zipline.pipeline.factors import Returns, AverageDollarVolume
from zipline import run_algorithm
MONTH, YEAR = 21, 252
N_LONGS = N_SHORTS = 25
VOL_SCREEN = 1000
class MeanReversion(CustomFactor):
"""Compute ratio of latest monthly return to 12m average,
normalized by std dev of monthly returns"""
inputs = [Returns(window_length=MONTH)]
window_length = YEAR
def compute(self, today, assets, out, monthly_returns):
df = pd.DataFrame(monthly_returns)
out[:] = df.iloc[-1].sub(df.mean()).div(df.std())
def compute_factors():
"""Create factor pipeline incl. mean reversion,
filtered by 30d Dollar Volume; capture factor ranks"""
mean_reversion = MeanReversion()
dollar_volume = AverageDollarVolume(window_length=30)
return Pipeline(columns={'longs' : mean_reversion.bottom(N_LONGS),
'shorts' : mean_reversion.top(N_SHORTS),
'ranking':
mean_reversion.rank(ascending=False)},
screen=dollar_volume.top(VOL_SCREEN))
结果将允许我们进行长期和短期订单的下达。在下一章中,我们将学习如何通过选择再平衡周期和在新信号到达时调整投资组合持仓来构建投资组合。
initialize() 方法注册了 compute_factors() 管道,而 before_trading_start() 方法确保管道每天运行一次。record() 函数将管道的排名列以及当前资产价格添加到由 run_algorithm() 函数返回的性能 DataFrame 中:
def initialize(context):
"""Setup: register pipeline, schedule rebalancing,
and set trading params"""
attach_pipeline(compute_factors(), 'factor_pipeline')
def before_trading_start(context, data):
"""Run factor pipeline"""
context.factor_data = pipeline_output('factor_pipeline')
record(factor_data=context.factor_data.ranking)
assets = context.factor_data.index
record(prices=data.current(assets, 'price'))
最后,在 UTC 术语中定义 start 和 end 时间戳对象,设置资本基础,并执行带有对关键执行方法的引用的 run_algorithm()。性能 DataFrame 包含嵌套数据,例如,价格列由每个单元格的 pd.Series 组成。因此,当以 pickle 格式存储时,后续数据访问更容易:
start, end = pd.Timestamp('2015-01-01', tz='UTC'), pd.Timestamp('2018-
01-01', tz='UTC')
capital_base = 1e7
performance = run_algorithm(start=start,
end=end,
initialize=initialize,
before_trading_start=before_trading_start,
capital_base=capital_base)
performance.to_pickle('single_factor.pickle')
我们将在下一节中使用存储在 performance DataFrame 中的因子和定价数据来评估不同持有期的因子表现,但首先,我们将看一下如何通过组合 Quantopian 平台上多样数据源的几个 alpha 因子来创建更复杂的信号。
内置 Quantopian 因子
附带的笔记本 factor_library_quantopian.ipynb 包含了许多示例因子,这些因子要么由 Quantopian 平台提供,要么通过从 Jupyter Notebook 使用研究 API 访问的数据源计算而来。
有一些内置因子可以与量化 Python 库(特别是 NumPy 和 pandas)结合使用,从广泛的相关数据源(如美国股票价格、Morningstar 基本面和投资者情绪)中派生出更复杂的因子。
例如,市销率可作为 Morningstar 基本面数据集的一部分获得。它可以作为将在我们介绍 Zipline 库时进一步描述的管道的一部分使用。
结合来自多种数据源的因子
Quantopian 研究环境专为快速测试预测性α因子而设计。该过程非常类似,因为它建立在 Zipline 之上,但提供了更丰富的数据源访问。下面的代码示例说明了如何计算α因子,不仅来自市场数据,如以前所做的那样,还来自基本数据和替代数据。有关详细信息,请参阅笔记本multiple_factors_quantopian_research.ipynb。
Quantopian 免费提供了数百个 Morningstar 基本面变量,并将 Stocktwits 信号作为替代数据源的示例。还有一些自定义的宇宙定义,比如QTradableStocksUS,它应用了几个过滤器来限制回测宇宙,使其仅包括在现实市场条件下可能可交易的股票:
from quantopian.research import run_pipeline
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data.morningstar import income_statement,
operation_ratios, balance_sheet
from quantopian.pipeline.data.psychsignal import stocktwits
from quantopian.pipeline.factors import CustomFactor,
SimpleMovingAverage, Returns
from quantopian.pipeline.filters import QTradableStocksUS
我们将使用自定义的AggregateFundamentals类来使用最后报告的基本数据点。这旨在解决基本面数据每季度报告一次的事实,而 Quantopian 目前没有提供一种轻松的方法来聚合历史数据,比如说,在滚动基础上获取过去四个季度的总和:
class AggregateFundamentals(CustomFactor):
def compute(self, today, assets, out, inputs):
out[:] = inputs[0]
我们将再次使用前面代码中的自定义MeanReversion因子。我们还将使用rank()方法的mask参数来计算给定宇宙定义的几个其他因子的排名:
def compute_factors():
universe = QTradableStocksUS()
profitability = (AggregateFundamentals(inputs=
[income_statement.gross_profit],
window_length=YEAR) /
balance_sheet.total_assets.latest).rank(mask=universe)
roic = operation_ratios.roic.latest.rank(mask=universe)
ebitda_yield = (AggregateFundamentals(inputs=
[income_statement.ebitda],
window_length=YEAR) /
USEquityPricing.close.latest).rank(mask=universe)
mean_reversion = MeanReversion().rank(mask=universe)
price_momentum = Returns(window_length=QTR).rank(mask=universe)
sentiment = SimpleMovingAverage(inputs=[stocktwits.bull_minus_bear],
window_length=5).rank(mask=universe)
factor = profitability + roic + ebitda_yield + mean_reversion +
price_momentum + sentiment
return Pipeline(
columns={'Profitability' : profitability,
'ROIC' : roic,
'EBITDA Yield' : ebitda_yield,
"Mean Reversion (1M)": mean_reversion,
'Sentiment' : sentiment,
"Price Momentum (3M)": price_momentum,
'Alpha Factor' : factor})
此算法简单地将每个资产的六个个别因子的排名平均起来,以结合它们的信息。这是一种相当简单的方法,它并未考虑到每个因子在预测未来收益时可能提供的相对重要性和增量信息。下一章的机器学习算法将使我们能够使用相同的回测框架来做到这一点。
执行还依赖于run_algorithm(),但 Quantopian 平台上的returnDataFrame 仅包含由Pipeline创建的因子值。这很方便,因为这种数据格式可以用作 Alphalens 的输入,该库用于评估α因子的预测性能。
使用 TA-Lib 与 Zipline
TA-Lib 库包含众多技术因素。Python 实现可供本地使用,例如,与 Zipline 和 Alphalens 一起使用,它还可在 Quantopian 平台上使用。该笔记本还说明了使用 TA-Lib 可用的几个技术指标。
使用 Alphalens 分离信号与噪声
Quantopian 已经开源了用于预测性股票因子的性能分析的 Python Alphalens 库。它与 Zipline 回测库以及投资组合性能和风险分析库 pyfolio 很好地集成在一起,我们将在下一章中探讨。
Alphalens 便于分析 alpha 因子的预测能力,涉及:
-
信号与随后收益的相关性
-
基于(子集的)信号的等权或因子加权组合的盈利能力
-
因子的周转率,以指示潜在的交易成本
-
特定事件期间的因子表现
-
前面的按行业细分
分析可以使用纸质文档或个别计算和绘图进行。纸质文档在在线存储库中有图示,以节省一些空间。
创建前向收益和因子分位数
要使用 Alphalens,我们需要提供两个输入:
-
资产组合的信号,例如
MeanReversion因子的排名返回的那些信号 -
我们投资于某一资产的给定持有期将获得的前向收益
有关详细信息,请参阅笔记本 06_performance_eval_alphalens.ipynb。
我们将从 single_factor.pickle 文件中恢复价格如下(并对factor_data 以同样方式进行处理;参见笔记本):
performance = pd.read_pickle('single_factor.pickle')
prices = pd.concat([df.to_frame(d) for d, df in performance.prices.items()],axis=1).T
prices.columns = [re.findall(r"\[(.+)\]", str(col))[0] for col in
prices.columns]
prices.index = prices.index.normalize()
prices.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 755 entries, 2015-01-02 to 2017-12-29
Columns: 1661 entries, A to ZTS
dtypes: float64(1661)
我们可以使用 get_clean_factor_and_forward_returns 实用函数,从 Zipline 输出中生成所需格式的 Alphalens 输入数据,即先前描述的因子信号和前向收益。该函数返回给定持有期的信号五分位和前向收益:
HOLDING_PERIODS = (5, 10, 21, 42)
QUANTILES = 5
alphalens_data = get_clean_factor_and_forward_returns(factor=factor_data,
prices=prices,
periods=HOLDING_PERIODS,
quantiles=QUANTILES)
Dropped 14.5% entries from factor data: 14.5% in forward returns computation and 0.0% in binning phase (set max_loss=0 to see potentially suppressed Exceptions). max_loss is 35.0%, not exceeded: OK!
alphalens_data DataFrame 包含给定日期投资于指定资产的收益和所示持有期的因子值,即该资产的 MeanReversion 在该日期的排名和相应的分位数值:
| date | asset | 5D | 10D | 21D | 42D | factor | factor_quantile |
|---|---|---|---|---|---|---|---|
| 1/2/2015 | A | -1.87% | -1.11% | -4.61% | 5.28% | 2618 | 4 |
| AAL | -0.06% | -8.03% | -9.63% | -10.39% | 1088 | 2 | |
| AAP | -1.32% | 0.23% | -1.63% | -2.39% | 791 | 1 | |
| AAPL | -2.82% | -0.07% | 8.51% | 18.07% | 2917 | 5 | |
| ABBV | -1.88% | -0.20% | -7.88% | -8.24% | 2952 | 5 |
前向收益和信号分位数是评估信号预测能力的基础。通常,因子应该为不同的分位数提供明显不同的回报,例如因子值底部五分位的负回报和顶部分位数的正回报。
因子分位数的预测性能
作为第一步,我们想要可视化因子分位数的平均期回报。我们可以使用性能模块中的内置函数 mean_return_by_quantile 和绘图模块中的 plot_quantile_returns_bar:
from alphalens.performance import mean_return_by_quantile
from alphalens.plotting import plot_quantile_returns_bar
mean_return_by_q, std_err = mean_return_by_quantile(alphalens_data)
plot_quantile_returns_bar(mean_return_by_q);
结果是一个条形图,根据因子信号的五分位将四个不同持有期的前向收益的平均值进行了拆分。
正如您在 图 4.9 中所见,除了最长持有期外,底部五分位的收益远远低于顶部五分位:
图 4.9:因子分位数的平均期回报
10D持有期在整个交易期间对第一和第四四分位提供了略微更好的结果。
我们还希望看到由每个信号五分位驱动的投资随时间的表现。为此,我们计算了5D持有期的日收益,而不是平均收益。Alphalens 调整了期间收益,以解决日信号和较长持有期之间的不匹配(有关详细信息,请参阅 Alphalens 文档):
from alphalens.plotting import plot_cumulative_returns_by_quantile
mean_return_by_q_daily, std_err =
mean_return_by_quantile(alphalens_data, by_date=True)
plot_cumulative_returns_by_quantile(mean_return_by_q_daily['5D'],
period='5D');
图 4.10中得到的线形图显示,在这 3 年的大部分时间里,前两个五分位明显优于后两个五分位。然而,正如前一个图所示,第四个五分位的信号由于其在 2017 年的相对绩效而产生了略微更好的绩效:
图 4.10:5 天持有期按分位累积收益
对于交易策略有用的因子表现出前述模式,其中累积收益沿着明显不同的路径发展,因为这允许具有较低资本要求和相应较低整体市场风险敞口的多头策略。
但是,我们还需要考虑期间收益的分散,而不仅仅是平均值。为此,我们可以依赖内置的plot_quantile_returns_violin:
from alphalens.plotting import plot_quantile_returns_violin
plot_quantile_returns_violin(mean_return_by_q_daily);
这个分布图,显示在图 4.11中,突出了日收益范围相当广泛。尽管有不同的均值,但分布的分离非常有限,以至于在任何给定的一天,不同五分位之间的绩效差异可能相当有限:
图 4.11:按因子五分位分布的期间收益
虽然我们专注于评估单个 alpha 因子,但在我们在下一章节中处理正确的回测时,我们通过忽略与交易执行相关的实际问题来简化事务。其中一些问题包括:
-
交易的交易成本
-
滑点,或者是决策价和交易执行价之间的差异,例如由于市场影响
信息系数
本书的大部分内容都是关于使用 ML 模型设计 alpha 因子。ML 是关于优化某些预测目标,而在本节中,我们将介绍用于衡量 alpha 因子性能的关键指标。我们将定义alpha为超过基准的平均收益。
这导致了信息比率(IR),它通过将 alpha 除以跟踪风险来衡量单位风险的平均超额收益。当基准是无风险利率时,IR 对应于著名的夏普比率,我们将强调在典型情况下,即收益不符合正态分布时出现的关键统计测量问题。我们还将解释主动管理的基本法则,该法则将 IR 分解为预测技能的组合以及策略有效利用这些预测技能的能力。
Alpha 因子的目标是准确预测未来收益的方向。因此,一个自然的绩效度量是 alpha 因子预测与目标资产未来收益之间的相关性。
最好使用非参数的 Spearman 秩相关系数,它衡量了两个变量之间的关系如何能够使用单调函数描述,而不是 Pearson 相关系数,后者衡量线性关系的强度。
我们可以使用 Alphalens 获取信息系数(IC),它依赖于底层的scipy.stats.spearmanr(请参阅存储库以了解如何直接使用scipy获取p-values 的示例)。factor_information_coefficient函数计算周期相关性,plot_ic_ts创建带有 1 个月移动平均线的时间序列图:
from alphalens.performance import factor_information_coefficient
from alphalens.plotting import plot_ic_ts
ic = factor_information_coefficient(alphalens_data)
plot_ic_ts(ic[['5D']])
图 4.12中的时间序列图显示了具有显著正移动平均 IC 的延长时期。如果有足够的机会应用这种预测技能,IC 为 0.05 甚至 0.1 可以实现显著的超额收益,正如主动管理的基本法则所说明的那样:
图 4.12:5 天视野的 IC 移动平均值
年均 IC 的图表突出显示了该因子的历史表现不均匀:
ic = factor_information_coefficient(alphalens_data)
ic_by_year = ic.resample('A').mean()
ic_by_year.index = ic_by_year.index.year
ic_by_year.plot.bar(figsize=(14, 6))
这产生了图表图 4.13:
图 4.13:按年份划分的 IC
如本例中所示,信息系数低于 0.05,但显著,并且相对于基准可以产生正的残差收益,我们将在下一节中看到。命令create_summary_tear_sheet(alphalens_data)创建 IC 摘要统计。
风险调整 IC 的结果是通过将 IC 均值除以 IC 的标准差来得到的,还要经受双边t检验,零假设为IC = 0,使用scipy.stats.ttest_1samp:
| 5 天 | 10 天 | 21 天 | 42 天 | |
|---|---|---|---|---|
| IC 均值 | 0.021 | 0.025 | 0.015 | 0.001 |
| IC 标准差 | 0.144 | 0.13 | 0.12 | 0.12 |
| 风险调整 IC | 0.145 | 0.191 | 0.127 | 0.01 |
| t 值(IC) | 3.861 | 5.107 | 3.396 | 0.266 |
| p 值(IC) | 0 | 0 | 0.001 | 0.79 |
| IC 偏度 | 0.384 | 0.251 | 0.115 | 0.134 |
| IC 峰度 | 0.019 | -0.584 | -0.353 | -0.494 |
因子周转率
因子周转率衡量了与特定分位数关联的资产频繁变动的频率,即调整投资组合以适应信号序列所需的交易次数。更具体地说,它衡量了当前处于因子分位数中的资产份额,而上一期不在该分位数中的资产份额。以下表格由此命令生成:
create_turnover_tear_sheet(alphalens_data)
加入基于五分位数的投资组合的资产份额相当高,这表明交易成本对于收获预测性能的好处构成了一项挑战:
| 平均周转率 | 5D | 10D | 21D | 42D |
|---|---|---|---|---|
| 四分位数 1 | 0.587 | 0.826 | 0.828 | 0.41 |
| 四分位数 2 | 0.737 | 0.801 | 0.81 | 0.644 |
| 四分位数 3 | 0.764 | 0.803 | 0.808 | 0.679 |
| 四分位数 4 | 0.737 | 0.803 | 0.808 | 0.641 |
| 四分位数 5 | 0.565 | 0.802 | 0.809 | 0.393 |
因子周转的另一个视角是由于因子的资产排名在不同持有期间的相关性,也是撕裂表的一部分:
| 5D | 10D | 21D | 42D | |
|---|---|---|---|---|
| 平均因子等级自相关性 | 0.713 | 0.454 | -0.011 | -0.016 |
通常,更多的稳定性更可取,以保持交易成本可控。
Alpha 因子资源
研究过程需要根据其信号的预测能力设计和选择 Alpha 因子。算法交易策略通常会基于多个为每个资产发送信号的 Alpha 因子构建。这些因子可以使用 ML 模型聚合,以优化各种信号如何转化为关于个别仓位的时间和大小决策,正如我们将在后续章节中看到的那样。
替代算法交易库
用于算法交易和数据收集的其他开源 Python 库包括以下内容(请参阅 GitHub 链接):
-
QuantConnect是 Quantopian 的竞争对手。
-
WorldQuant提供在线竞赛,并招募社区贡献者参与众包式对冲基金。
-
Alpha Trading Labs提供了一个与 Quantopian 类似的高频率测试基础设施,并采用了相似的商业模式。
-
Python 算法交易库(PyAlgoTrade)专注于回测,并支持模拟交易和实时交易。它允许您使用历史数据评估交易策略的想法,并旨在以最小的努力实现这一目标。
-
pybacktest是一个矢量化的回测框架,使用 pandas,并旨在紧凑、简单且快速。(该项目目前暂停。)
-
ultrafinance是一个较老的项目,结合了实时财务数据收集和交易策略的分析和回测。
-
用 Python 进行交易提供了课程和一套用于量化交易的函数和类。
-
互动经纪商提供了一个用于在其平台上进行实时交易的 Python API。
摘要
在本章中,我们介绍了一系列α因子,这些因子已被专业投资者用于设计和评估策略数十年。我们阐述了它们的工作原理,并说明了一些被认为推动其绩效的经济机制。我们这样做是因为对因子如何产生超额收益有扎实的理解有助于创新新的因子。
我们还介绍了几种工具,您可以使用这些工具从各种数据源生成自己的因子,并演示了卡尔曼滤波器和小波如何使我们能够平滑嘈杂的数据,希望能够获得更清晰的信号。
最后,我们简要介绍了用于事件驱动交易算法的 Zipline 库,无论是离线还是在线于 Quantopian 平台上。您了解了如何实现一个简单的均值回归因子,以及如何简单地结合多个因子来驱动基本策略。我们还看了 Alphalens 库,该库允许评估信号的预测性能和交易换手率。
投资组合构建过程反过来则从更广泛的角度出发,旨在从风险和回报的角度对头寸进行最优定量。在下一章节中,投资组合优化与策略评估,我们将探讨在投资组合过程中平衡风险和回报的各种策略。我们还将更详细地研究在有限历史数据集上回测交易策略的挑战,以及如何解决这些挑战。
第五章:组合优化和绩效评估
阿尔法因子产生信号,算法策略将其转化为交易,进而产生多头和空头仓位。由此产生的投资组合的回报和风险决定了策略的成功。
在市场条件下测试策略之前,我们需要模拟算法将进行的交易并验证其绩效。策略评估包括针对历史数据进行反向测试以优化策略的参数,并进行向前测试以验证样本内绩效与新的样本外数据。目标是避免从针对特定过去情况量身定制策略中发现虚假结果。
在组合环境中,正资产回报可以抵消负价格波动。一个资产的正价格变动更有可能抵消另一个资产的损失,这两个仓位之间的相关性越低。基于仓位协方差对组合风险的影响,哈里·马科维茨于 1952 年开发了现代投资组合管理背后的理论,该理论基于多样化。结果是均值-方差优化,它为给定的资产集合选择权重以最小化风险,风险用预期收益的标准差来衡量。
资本资产定价模型(CAPM)引入了一个风险溢价,该溢价以超过无风险投资的预期收益来衡量,作为持有资产的平衡报酬。这种奖励是对持有资产暴露于一个系统性风险因素——市场的补偿,与特定资产特有的非系统性风险相对,因此无法通过分散化消除。
风险管理随着额外的风险因素和更精细的暴露选择的出现而变得更加复杂。凯利准则是动态组合优化的一种流行方法,即选择一系列随时间变化的仓位;它最初被爱德华·索普在 1968 年从赌博中著名地改编到股票市场中。
因此,有几种优化投资组合的方法,包括将机器学习(ML)应用于学习资产之间的层次关系,并将它们的持仓视为相互补充或替代品,以满足组合风险概况。
在本章中,我们将涵盖以下主题:
-
如何衡量组合风险和回报
-
使用均值-方差优化和替代方法管理组合权重
-
在组合环境中使用机器学习来优化资产配置
-
使用 Zipline 模拟交易并基于阿尔法因子创建投资组合
-
如何使用 pyfolio 评估组合绩效
您可以在 GitHub 存储库的相应目录中找到本章的代码示例和其他资源的链接。笔记本包括图像的彩色版本。
如何衡量组合绩效
为了评估和比较不同的策略或改进现有策略,我们需要反映它们相对于我们目标的表现的指标。在投资和交易中,最常见的目标是投资组合的回报和风险。
通常,这些指标与代表替代投资机会的基准进行比较,例如作为美国股票的摘要的投资宇宙的标准普尔 500 或固定收益资产的无风险利率。
有几种指标可用于评估这些目标。在本节中,我们将回顾比较投资组合结果最常见的度量标准。当我们研究不同的方法来优化投资组合绩效,使用 Zipline 模拟策略与市场的交互,并在后续章节中使用 pyfolio 库计算相关绩效指标时,这些措施将会很有用。
我们将使用一些简单的符号:设 R 为单期简单投资组合回报的时间序列,R=(r[1], ..., r[T]),从日期 1 到 T,并且 R^f =(r^f[1], ..., r^f[T]) 为相匹配的无风险利率的时间序列,因此 R[e]=R-R[f] =(r[1]-r^f[1],..., r[T]-r^f[T]) 是超额回报。
用一个数字捕捉风险-回报权衡关系
回报和风险目标暗示着一种权衡关系:在某些情况下,承担更多风险可能会带来更高的回报,但也意味着更大的下行风险。为了比较不同策略如何权衡这一权衡关系,非常流行的是计算单位风险回报率的比率。我们将依次讨论夏普比率和信息比率。
夏普比率
夏普比率(SR)前瞻性地比较了投资组合的预期超额回报与该超额回报的波动性,由其标准偏差测量。它衡量了作为单位风险承担的平均超额回报作为补偿:
预期回报和波动性不可观察,但可以根据历史数据如下估算:
除非无风险利率波动较大(如新兴市场),超额回报和原始回报的标准偏差将相似。
对于独立同分布(IID)的回报,用于统计显著性检验的 SR 估计量的分布,根据大样本统计理论,可以从中心极限定理(CLT)的应用得出,这意味着像 和
这样的 IID 随机变量的和收敛于正态分布。
当您需要比较不同频率的夏普比率时,例如月度和年度数据,您可以将更高频率的夏普比率乘以包含在更低频率中的对应时期的平方根。要将月度夏普比率转换为年度夏普比率,请乘以,要将日度转换为月度,请乘以
。
然而,金融回报往往违反独立同分布假设。安德鲁·罗已经推导出了对分布和时间聚合进行必要调整的公式,以适应平稳但自相关的回报。这很重要,因为投资策略的时间序列特性(例如,均值回归,动量和其他形式的序列相关性)可能对夏普比率估计本身产生非平凡的影响,特别是当从高频数据年化夏普比率时(Lo,2002)。
信息比率
信息比率(IR)类似于夏普比率,但使用的是基准而不是无风险利率。基准通常被选为代表可用投资领域的投资组合,如标普 500 指数用于大型美国股票投资组合。
因此,信息比率(IR)衡量的是投资组合的超额回报,也称为阿尔法,相对于跟踪误差,即投资组合回报与基准回报之间的偏差,即:
IR 还被用来解释超额回报如何取决于管理者的技能和她的策略的性质,我们将在下文中看到。
主动管理的基本定律
“分散化是对无知的防护。如果你知道你在做什么,那就没多大意义。”
– 沃伦·巴菲特
一个好奇的事实是,文艺复兴技术(RenTec),由吉姆·西蒙斯创立的表现最佳的量化基金,在第一章,交易的机器学习-从构想到执行中提到过,尽管采取了极其不同的方法,却产生了与沃伦·巴菲特相似的回报。沃伦·巴菲特的投资公司伯克希尔·哈撒韦持有大约 100-150 只股票,持有时间相当长,而 RenTec 可能每天执行 10 万笔交易。我们如何比较这些不同的策略?
高 IR 反映了相对于承担的额外风险而言,基准的吸引人的超额表现。主动管理的基本定律解释了如何实现这样的结果:它将 IR 近似为信息系数(IC)和策略的广度的乘积。
正如前一章所讨论的,IC 衡量了回报预测之间的等级相关性,例如由阿尔法因子暗示的预期回报与实际未来回报之间的相关性。因此,它是管理者的预测技能的一种度量。策略的广度由投资者在给定时间段内进行的独立下注数(即交易数)来衡量,从而代表了应用预测技能的能力。
基本法律规定,IR,也称为评估风险(特雷纳和布莱克),是两个值的乘积。换句话说,它总结了频繁(高广度)和出色表现(高 IC)的重要性:
这个框架已经扩展到包括转移系数(TC)来反映投资组合约束作为一个额外因素(例如,对做空的限制),这可能会限制信息比率在否则可达到的水平以下的因素。 TC 代表了管理者将见解转化为投资组合投注的效率:如果没有约束,TC 将简单地等于一;但是如果管理者尽管预测表明他们应该这样做而不做空股票,TC 将小于一,并减少 IC(Clarke 等人,2002)。
基本法律之所以重要,是因为它突出了超额表现的关键驱动因素:准确预测和能够进行独立预测并在这些预测上采取行动都很重要。
在实践中,拥有广泛投资决策的管理人员可以通过信息系数在 0.05 至 0.15 之间实现显著的风险调整超额回报,如下模拟所示:
图 5.1:不同广度和信息系数值的信息比率
在实践中,鉴于预测之间的横截面和时间序列相关性,估计策略的广度是困难的。您应该将基本法律及其扩展视为一个有用的分析框架,用于思考如何改善您的风险调整后的投资组合绩效。接下来我们将看一些实践中做到这一点的技术。
如何管理投资组合风险和回报
投资组合管理旨在挑选和规模化金融工具中的头寸,以实现与基准相关的期望风险回报权衡。作为投资组合经理,在每个时期,您选择优化多样化的头寸,以降低风险并实现目标回报。在不同时期,这些头寸可能需要重新平衡,以考虑价格波动导致的权重变化,以实现或维持目标风险配置。
现代投资组合管理的演变
多样化使我们能够通过利用不完美的相关性,使一个资产的收益弥补另一个资产的损失,以降低给定预期回报的风险。哈里·马科维茨于 1952 年发明了现代投资组合理论(MPT),并提供了通过选择适当的投资组合权重来优化多样化的数学工具。
马科维茨展示了投资组合风险,以投资组合收益的标准偏差衡量,如何取决于所有资产收益之间的协方差以及它们的相对权重。这种关系意味着存在一组投资组合的有效前沿,其最大化了在给定最大化投资组合风险水平的情况下的投资组合回报。
然而,均值-方差前沿对于其计算所需的输入的估计非常敏感,这些输入包括预期回报、波动率和相关性。在实践中,将这些输入约束在一定范围内以减少抽样误差的均值-方差组合投资组合表现更好。这些受限制的特殊情况包括等权重、最小方差和风险平价投资组合。
资本资产定价模型(CAPM)是建立在 MPT 风险-回报关系基础上的资产估值模型。它引入了一个概念,即投资者在市场均衡状态下持有风险资产时可以期望的风险溢价;该溢价补偿了货币时间价值和无法通过分散化消除的整体市场风险暴露(与特定资产的特殊风险相对)。
不可分散风险的经济基础包括,例如,影响所有股票回报或债券违约的业务风险的宏观驱动因素。因此,资产的预期回报E[r[i]]是无风险利率r[f]和与资产暴露于市场组合预期超额回报r[m]的风险溢价的乘积的总和:
理论上,市场组合包含所有可投资资产,并且在均衡状态下,将由所有理性投资者持有。在实践中,一个广义的价值加权指数近似于市场,例如,对于美国股票投资来说是标准普尔 500 指数(S&P 500)。
测量了资产i对市场组合超额回报的暴露。如果 CAPM 有效,则截距部分
应为零。实际上,CAPM 的假设通常未得到满足,而α捕捉了通过暴露于广泛市场而未解释的回报。
正如前一章所讨论的,随着时间的推移,研究揭示了非传统风险溢价,例如动量或股票价值效应,这些溢价解释了一些原始α。经济原理,如投资者对新信息的欠反应或过度反应的行为偏差,为暴露于这些替代风险因素的风险溢价提供了合理解释。
这些因素演变成了旨在捕捉这些替代贝塔的投资风格,这些替代贝塔可以通过专门的指数基金进行交易。同样,风险管理现在旨在控制除市场组合以外的多种风险来源的暴露。
在分离这些替代风险溢价的贡献后,真正的α仅限于特定资产回报和管理者调整风险暴露的能力。
有效市场假说(EMH)在过去几十年中得到了完善,以纠正 CAPM 的许多原始缺陷,包括信息不完全以及与交易、融资和代理相关的成本。许多行为偏见具有相同的效果,一些摩擦被建模为行为偏见。
过去几十年来,现代投资组合理论和实践发生了重大变化。我们将介绍几种方法:
-
均值-方差优化及其缺点
-
最小风险和 1/n 分配等替代方案
-
风险平价方法
-
风险因子方法
均值-方差优化
现代投资组合理论解决了为最小化给定预期收益的波动性或为给定波动性水平最大化收益的最优投资组合权重。关键的先决条件输入是预期资产收益率、标准偏差和协方差矩阵。
它的工作原理
多样化的作用是因为投资组合收益的方差取决于资产的协方差。通过包含相关性不完全的资产,可以将其降低到资产方差的加权平均值以下。
特别是,给定投资组合权重向量 和协方差矩阵
,投资组合方差
定义为:
马科维茨表明,最大化预期投资组合收益的问题,在目标风险约束下具有等效的最小化投资组合风险的对偶表示,主要是在目标预期收益水平下,。因此,优化问题变为:
在 Python 中找到有效边界
我们可以使用 scipy.optimize.minimize 和资产收益率、标准偏差以及协方差矩阵的历史估计来计算有效边界。 SciPy 的 minimize 函数实现了一系列标量函数的受约束和无约束优化算法,这些函数从一个或多个输入变量中输出一个单一数字(详见 SciPy 文档以获取更多详情)。代码可以在本章的存储库的 strategy_evaluation 子文件夹中找到,并实现以下步骤序列:
首先,模拟使用狄利克雷分布生成随机权重,并使用历史回报数据计算每个样本投资组合的均值、标准偏差和 SR:
def simulate_portfolios(mean_ret, cov, rf_rate=rf_rate, short=True):
alpha = np.full(shape=n_assets, fill_value=.05)
weights = dirichlet(alpha=alpha, size=NUM_PF)
if short:
weights *= choice([-1, 1], size=weights.shape)
returns = weights @ mean_ret.values + 1
returns = returns ** periods_per_year - 1
std = (weights @ monthly_returns.T).std(1)
std *= np.sqrt(periods_per_year)
sharpe = (returns - rf_rate) / std
return pd.DataFrame({'Annualized Standard Deviation': std,
'Annualized Returns': returns,
'Sharpe Ratio': sharpe}), weights
接下来,我们设置二次优化问题,以解决给定收益的最小标准偏差或最大 SR。为此,我们定义了测量关键绩效指标的函数:
def portfolio_std(wt, rt=None, cov=None):
"""Annualized PF standard deviation"""
return np.sqrt(wt @ cov @ wt * periods_per_year)
def portfolio_returns(wt, rt=None, cov=None):
"""Annualized PF returns"""
return (wt @ rt + 1) ** periods_per_year - 1
def portfolio_performance(wt, rt, cov):
"""Annualized PF returns & standard deviation"""
r = portfolio_returns(wt, rt=rt)
sd = portfolio_std(wt, cov=cov)
return r, sd
接下来,我们定义一个目标函数,表示 scipy 的 minimize 函数要优化的负 SR,考虑到权重受到的约束为 [0, 1],并且绝对值总和为一:
def neg_sharpe_ratio(weights, mean_ret, cov):
r, sd = portfolio_performance(weights, mean_ret, cov)
return -(r - rf_rate) / sd
weight_constraint = {'type': 'eq',
'fun': lambda x: np.sum(np.abs(x)) - 1}
def max_sharpe_ratio(mean_ret, cov, short=False):
return minimize(fun=neg_sharpe_ratio,
x0=x0,
args=(mean_ret, cov),
method='SLSQP',
bounds=((-1 if short else 0, 1),) * n_assets,
constraints=weight_constraint,
options={'tol':1e-10, 'maxiter':1e4})
然后,我们通过迭代一系列目标回报并解决相应的最小方差组合来计算有效前沿。为此,我们使用对投资组合风险和回报的约束作为权重的函数来制定优化问题,如下所示:
def min_vol_target(mean_ret, cov, target, short=False):
def ret_(wt):
return portfolio_returns(wt, mean_ret)
constraints = [{'type': 'eq', 'fun': lambda x: ret_(x) - target},
weight_constraint]
bounds = ((-1 if short else 0, 1),) * n_assets
return minimize(portfolio_std, x0=x0, args=(mean_ret, cov),
method='SLSQP', bounds=bounds,
constraints=constraints,
options={'tol': 1e-10, 'maxiter': 1e4})
解决方案需要迭代在可接受值范围内的范围,以确定最佳的风险-回报组合:
def efficient_frontier(mean_ret, cov, ret_range):
return [min_vol_target(mean_ret, cov, ret) for ret in ret_range]
模拟产生了一部分可行的投资组合,有效前沿确定了样本内可实现的最佳回报-风险组合,给定历史数据。
图 5.2显示了结果,包括最小方差组合、最大化 SR 的组合,以及几个由替代优化策略产生的组合,我们将在接下来的章节中讨论:
图 5.2:有效前沿和不同的优化组合
投资组合优化可以在交易策略的每个评估步骤中运行,以优化仓位。
挑战和缺点
前面的均值方差边界估计说明了样本内,也就是向后看优化。在实践中,投资组合优化需要前瞻性的输入和输出。然而,预期收益的准确估计是非常困难的。最好将其视为众多改进的起点和基准。
协方差矩阵的估计可能会更可靠一些,这导致了几种替代方法的出现。然而,具有相关资产的协方差矩阵会带来计算上的挑战,因为优化问题需要矩阵求逆。高条件数会导致数值不稳定性,从而产生马尔可夫茨诅咒:需要更多分散(通过相关的投资机会)时,算法产生的权重就越不可靠。
许多投资者更喜欢使用具有较少繁琐输入要求的投资组合优化技术。我们现在将介绍几种旨在解决这些缺点的替代方法,包括一种基于机器学习的较新方法。
均值方差优化的替代方案
对均值方差优化问题的准确输入的挑战已经导致了几种实用的替代方案的采用,这些方案限制了均值、方差或两者,或者省略了更具挑战性的回报估计,例如风险平价方法,我们将在本节后面讨论。
1/N 投资组合
简单的投资组合提供了用于衡量生成过拟合风险的复杂模型的增值的有用基准。最简单的策略——等权重投资组合——已被证明是最佳表现之一。
闻名的 de Miguel、Garlappi 和 Uppal(2009)比较了由各种均值-方差优化器产生的投资组合在样本外的表现,包括健壮的贝叶斯估计器、投资组合约束和投资组合的最优组合,与简单的 1/N 规则。他们发现,1/N 投资组合在各种数据集上产生了比替代方案更高的夏普比率,这可以解释为估计误差的高成本通常超过了样本外复杂优化的好处。
更具体地说,他们发现,对于具有 25 个资产的投资组合,样本为基础的均值-方差策略及其延伸优于 1/N 基准所需的估计窗口约为 3,000 个月,而对于具有 50 个资产的投资组合约为 6,000 个月。
在前一节的图 5.2中还包括了 1/N 投资组合。
最小方差投资组合
另一种选择是全局最小方差(GMV)投资组合,它优先考虑风险的最小化。它在图 5.2中显示,并可以通过使用均值-方差框架,如下所示,通过最小化投资组合标准偏差来计算:
def min_vol(mean_ret, cov, short=False):
return minimize(fun=portfolio_std,
x0=x0,
args=(mean_ret, cov),
method='SLSQP',
bounds=bounds = ((-1 if short else 0, 1),) *
n_assets,
constraints=weight_constraint,
options={'tol': 1e-10, 'maxiter': 1e4})
相应的最小波动率投资组合位于有效前沿上,正如之前在图 5.2中所示。
全球投资组合优化 – Black-Litterman 方法
Black 和 Litterman(1992)的全球投资组合优化方法将经济模型与统计学习相结合。它很受欢迎,因为它能够在许多情况下生成合理的预期收益估计。
该技术假定市场是一个均值-方差投资组合,正如 CAPM 平衡模型所暗示的那样。它建立在这样一个事实上,即观察到的市场资本化可以被视为市场对每个证券分配的最佳权重。市场权重反映了市场价格,而市场价格反过来又体现了市场对未来收益的预期。
该方法因此可以从市场足够接近 CAPM 定义的均衡的假设中逆向推导出不可观察的未来预期收益。投资者可以使用缩小估计器将这些估计值调整到自己的信念。该模型可以被解释为投资组合优化的贝叶斯方法。我们将在第十章,贝叶斯 ML – 动态夏普比率和配对交易策略中介绍贝叶斯方法。
如何确定你的赌注大小 – 凯利准则
凯利准则在赌博界有着悠久的历史,因为它提供了在具有不同(但有利)赔率的(无限)一系列赌注中如何押注以最大化期末财富的指导。它是由 John Kelly 在 1956 年的论文信息速率的新解释中发表的,他是 Claude Shannon 在贝尔实验室的同事。他对在新的问答节目“$64,000 问题”上对候选人的投注感兴趣,其中一位西海岸的观众利用三小时的延迟获取内幕信息来获取胜利者。
凯利将与香农的信息论联系起来,以解决在赔率有利但不确定性仍然存在时,长期资本增长的最佳赌注。他的准则最大化对每个游戏成功概率的对数财富,并包括隐含的破产保护,因为*log(0)*为负无穷,因此凯利赌徒自然会避免失去一切。
投注的最佳大小
凯利从分析具有二元赢输结果的游戏开始。关键变量包括:
-
b:定义了每次赌注赢得的金额的赔率。赔率= 5/1 意味着如果赌注获胜,则获得 5 美元,加上 1 美元的资本返还。
-
p:定义有利结果可能性的概率。
-
f:要下注的当前资本份额。
-
V:由于赌注而产生的资本价值。
凯利准则旨在最大化无限重复赌注的价值增长率G:
当W和L分别为赢得和输掉的次数时,则:
我们可以通过最大化G关于f的变化来最大化增长率G,使用 SymPy 进行说明,如下所示(您可以在kelly_rule笔记本中找到此内容):
from sympy import symbols, solve, log, diff
share, odds, probability = symbols('share odds probability')
Value = probability * log(1 + odds * share) + (1 - probability) * log(1
- share)
solve(diff(Value, share), share)
[(odds*probability + probability - 1)/odds]
我们得出了要下注的资本的最佳份额:
最佳投资 - 单资产
在金融市场背景下,结果和替代方案都更加复杂,但凯利准则的逻辑仍然适用。它由 Ed Thorp 流行起来,他首先将其成功地应用于赌博(在书籍Beat the Dealer中描述),后来成立了成功的对冲基金 Princeton/Newport Partners。
对于连续的结果,资本的增长率由对可能的不同回报的概率分布的积分来定义,可以通过数值优化来优化:
我们可以使用scipy.optimize模块解决此表达式以得出最优的f**。quad函数使用 FORTRAN 的 QUADPACK 库计算在两个值a和b*之间的定积分值(因此得名)。它返回积分值和误差估计:
def norm_integral(f, m, st):
val, er = quad(lambda s: np.log(1+f*s)*norm.pdf(s, m, st), m-3*st,
m+3*st)
return -val
def norm_dev_integral(f, m, st):
val, er = quad(lambda s: (s/(1+f*s))*norm.pdf(s, m, st), m-3*st,
m+3*st)
return val
m = .058
s = .216
# Option 1: minimize the expectation integral
sol = minimize_scalar(norm_integral, args=(
m, s), bounds=[0., 2.], method='bounded')
print('Optimal Kelly fraction: {:.4f}'.format(sol.x))
Optimal Kelly fraction: 1.1974
最佳投资 - 多个资产
我们将使用一个包含各种权益的示例。E. Chan(2008)说明了如何得出凯利准则的多资产应用,结果等同于(可能是杠杆的)从均值-方差优化中得出的最大夏普比率投资组合。
计算涉及精度矩阵(协方差矩阵的逆)与回报矩阵的点积:
mean_returns = monthly_returns.mean()
cov_matrix = monthly_returns.cov()
precision_matrix = pd.DataFrame(inv(cov_matrix), index=stocks, columns=stocks)
kelly_wt = precision_matrix.dot(mean_returns).values
凯利投资组合也显示在之前的有效边界图中(标准化以使绝对权重之和等于一)。许多投资者更喜欢减少凯利权重以降低该策略的波动性,而半凯利则变得特别受欢迎。
风险平价
过去 15 年的两次全球股票市场危机、一直呈上升趋势的收益率曲线以及利率的普遍下降,使得风险平价看起来成为一种特别引人注目的选择。许多机构将战略性配置给了风险平价以进一步实现投资组合的多样化。
风险平价的简单实现根据它们的方差的倒数分配资产,忽略了相关性,尤其是回报预测:
var = monthly_returns.var()
risk_parity_weights = var / var.sum()
风险平价投资组合也显示在本节开头的有效边界图中。
风险因子投资
估算输入的另一种替代框架是向下工作到驱动资产风险和回报的基础决定因素或因子。如果我们了解这些因素如何影响回报,我们又了解这些因素,我们将能够构建更健壮的投资组合。
因子投资的概念超越了资产类别标签。它着眼于我们在上一章关于 alpha 因子中讨论过的基础因子风险,以最大程度地实现分散化的利益。因子投资不是通过诸如对冲基金或私募股权之类的标签来区分投资工具,而是旨在根据暴露于基本风险因子的差异来识别不同的风险-回报配置(Ang 2014)。
对于均值-方差投资的天真方法将(人为的)分组视为不同的资产类别并纳入均值-方差优化器中。因子投资认识到这些分组与传统资产类别共享许多相同的因子风险。分散化的好处可能被夸大,正如投资者在 2008 年危机期间发现的那样,当风险资产类别之间的相关性增加时,由于暴露于相同的基础因子风险,相关性增加。
在第七章,线性模型-从风险因素到收益预测中,我们将展示如何测量投资组合对各种风险因素的暴露,以便您可以调整头寸以调整您的因子敞口,或者相应地进行对冲。
分层风险平价
均值-方差优化对预期收益的估计和这些收益的协方差非常敏感。当收益高度相关时,协方差矩阵的求逆也变得更具挑战性和不太准确,这在实践中经常是情况。结果被称为马科维茨诅咒:当分散化更为重要因为投资相关时,传统的投资组合优化器可能会产生不稳定的解决方案。分散化的好处可能会被错误的估计抵消。正如讨论的那样,甚至是天真的、等权重的投资组合都可以在样本外击败均值-方差和基于风险的优化。
更健壮的方法已经合并了额外的约束 (Clarke et al., 2002) 或贝叶斯先验 (Black and Litterman, 1992),或者使用收缩估计量使精度矩阵更加数值稳定 (Ledoit and Wolf, 2003),这在 scikit-learn 中可用 (scikit-learn.org/stable/modules/generated/sklearn.covariance.LedoitWolf.html)。
分层风险平价 (HRP) 相比之下,利用无监督机器学习来实现出色的样本外投资组合分配。 最近在投资组合优化方面的创新利用图论和层次聚类来构建投资组合分为三个步骤 (Lopez de Prado, 2015):
-
定义一个距离度量,使相关资产彼此接近,并应用单链接聚类来识别层次关系。
-
利用层次相关结构对协方差矩阵进行准对角化。
-
应用自顶向下的逆方差加权使用递归二分搜索将集群资产视为投资组合构建中的补充而不是替代品,并减少自由度数量。
构建分层聚类投资组合 (HCP) 的一种相关方法由 Raffinot (2016) 提出。 从概念上讲,诸如金融市场之类的复杂系统倾向于具有结构,并且通常以分层方式组织,而层次结构中元素之间的相互作用塑造了系统的动态。 相关矩阵也缺乏层次结构的概念,这使得权重可以自由变化并且可能以意想不到的方式变化。
JP Morgan (2012) 对各种股票投资组合进行了 HRP 和 HCP 的测试。 其中,HRP 尤其产生了与天真分散、最大分散投资组合或 GMV 投资组合相比相等或更优的风险调整后回报和夏普比率。
我们将在第十三章,使用无监督学习的数据驱动风险因素和资产配置中介绍 Python 实现。
使用 Zipline 进行交易和管理投资组合
在上一章中,我们介绍了 Zipline 来模拟对一系列股票的市场、基本和替代数据的 alpha 因子的计算。 在本节中,我们将开始根据 alpha 因子发出的信号行动起来。 我们将通过提交买入和卖出订单来做到这一点,以便我们可以建立多头和空头头寸或者重新平衡投资组合,以将我们的持仓调整到最新的交易信号。
我们将推迟优化投资组合权重至本章后面,并且,目前,只是为每个持仓分配相同价值的仓位。 正如前一章中所述,对包括 ML 模型的策略进行测试和评估的深入介绍将在第六章,机器学习流程中进行。
安排信号生成和交易执行
我们将使用前一章开发的自定义MeanReversion因子(请参见01_backtest_with_trades.ipynb中的实现)。
由compute_factors()方法创建的Pipeline返回一个包含 50 个多头和空头的列的表格。它根据其上个月回报率与年均值的最大负偏差和最大正偏差选择股票,标准差归一化:
def compute_factors():
"""Create factor pipeline incl. mean reversion,
filtered by 30d Dollar Volume; capture factor ranks"""
mean_reversion = MeanReversion()
dollar_volume = AverageDollarVolume(window_length=30)
return Pipeline(columns={'longs' : mean_reversion.bottom(N_LONGS),
'shorts' : mean_reversion.top(N_SHORTS),
'ranking': mean_reversion.rank(ascending=False)},
screen=dollar_volume.top(VOL_SCREEN))
它还将宇宙限制为过去 30 个交易日交易量最高的 1,000 只股票。before_trading_start()确保每日执行Pipeline和记录结果,包括当前价格:
def before_trading_start(context, data):
"""Run factor pipeline"""
context.factor_data = pipeline_output('factor_pipeline')
record(factor_data=context.factor_data.ranking)
assets = context.factor_data.index
record(prices=data.current(assets, 'price'))
新的rebalance()方法向由Pipeline标记为多头和空头头寸的资产提交交易订单,权重相等并且为正负值。它还剥离任何当前持仓,这些持仓不再包含在因子信号中:
def exec_trades(data, assets, target_percent):
"""Place orders for assets using target portfolio percentage"""
for asset in assets:
if data.can_trade(asset) and not get_open_orders(asset):
order_target_percent(asset, target_percent)
def rebalance(context, data):
"""Compute long, short and obsolete holdings; place trade orders"""
factor_data = context.factor_data
assets = factor_data.index
longs = assets[factor_data.longs]
shorts = assets[factor_data.shorts]
divest = context.portfolio.positions.keys() - longs.union(shorts)
exec_trades(data, assets=divest, target_percent=0)
exec_trades(data, assets=longs, target_percent=1 / N_LONGS if N_LONGS
else 0)
exec_trades(data, assets=shorts, target_percent=-1 / N_SHORTS if N_SHORTS
else 0)
rebalance()方法根据schedule_function()工具在周初开始,紧接着market_open运行,正如内置的US_EQUITIES日历所规定的那样(有关规则的详细信息,请参阅 Zipline 文档)。
您还可以指定交易佣金,既以相对比例,又以最小金额。还有一种定义滑点的选项,即交易决策与执行之间价格不利变化的成本:
def initialize(context):
"""Setup: register pipeline, schedule rebalancing,
and set trading params"""
attach_pipeline(compute_factors(), 'factor_pipeline')
schedule_function(rebalance,
date_rules.week_start(),
time_rules.market_open(),
calendar=calendars.US_EQUITIES)
set_commission(us_equities=commission.PerShare(cost=0.00075,
min_trade_cost=.01))
set_slippage(us_equities=slippage.VolumeShareSlippage(volume_limit=0.0025, price_impact=0.01))
调用run_algorithm()函数后,算法继续执行,并返回我们在前一章中看到的相同的回测性能DataFrame。
实施均值方差投资组合优化
我们在上一节中演示了如何使用scipy.optimize找到有效前沿。在本节中,我们将利用 PyPortfolioOpt 库,该库提供投资组合优化(使用 SciPy 作为内部支持),包括有效前沿技术和更近期的收缩方法,用于正则化协方差矩阵(请参见第七章,线性模型 - 从风险因素到收益预测,关于线性回归的收缩)。代码示例位于02_backtest_with_pf_optimization.ipynb中。
我们将使用从MeanReversion因子排名派生的 50 个多头和空头头寸相同的设置。rebalance()函数接收建议的多头和空头头寸,并将每个子集传递给新的optimize_weights()函数以获得asset: target_percent对的字典:
def rebalance(context, data):
"""Compute long, short and obsolete holdings; place orders"""
factor_data = context.factor_data
assets = factor_data.index
longs = assets[factor_data.longs]
shorts = assets[factor_data.shorts]
divest = context.portfolio.positions.keys() - longs.union(shorts)
exec_trades(data, positions={asset: 0 for asset in divest})
# get price history
prices = data.history(assets, fields='price',
bar_count=252+1, # 1 yr of returns
frequency='1d')
if len(longs) > 0:
long_weights = optimize_weights(prices.loc[:, longs])
exec_trades(data, positions=long_weights)
if len(shorts) > 0:
short_weights = optimize_weights(prices.loc[:, shorts], short=True)
exec_trades(data, positions=short_weights)
optimize_weights()函数使用PyPortfolioOpt提供的EfficientFrontier对象,找到基于最近一年的收益和协方差矩阵的最大夏普比率的权重,库也计算这两个值:
def optimize_weights(prices, short=False):
returns = expected_returns.mean_historical_return(prices=prices,
frequency=252)
cov = risk_models.sample_cov(prices=prices, frequency=252)
# get weights that maximize the Sharpe ratio
ef = EfficientFrontier(expected_returns=returns,
cov_matrix=cov,
weight_bounds=(0, 1),
gamma=0)
weights = ef.max_sharpe()
if short:
return {asset: -weight for asset, weight in ef.clean_weights().items()}
else:
return ef.clean_weights()
它返回归一化的权重,总和为 1,对于空头头寸设置为负值。
图 5.3 表明,对于这个特定的策略和时间范围,均值方差优化的投资组合表现明显更好:
图 5.3: 均值方差 vs 等权重投资组合表现
PyPortfolioOpt 还可以找到最小波动率的投资组合。更普遍地说,这个例子说明了如何使用前一节介绍的方法,或者您选择的任何其他方法,添加逻辑来调整投资组合权重。
我们现在将转向投资组合回报和风险的常见衡量标准,以及如何使用 pyfolio 库来计算它们。
使用 pyfolio 测量回测性能
Pyfolio 便利地分析了投资组合性能,在和样本外都使用了丰富的指标和可视化。它生成了覆盖回报、仓位和交易分析的泪水表,以及在市场压力期间使用几种内置场景进行事件风险分析。它还包括贝叶斯样本外性能分析。
Pyfolio 依赖于投资组合回报和仓位数据,还可以考虑交易活动的交易成本和滑点损失。它使用 empyrical 库,该库也可以独立用于计算性能指标。
创建回报和基准输入
该库是 Quantopian 生态系统的一部分,并与 Zipline 和 Alphalens 兼容。我们首先将演示如何从 Alphalens 生成所需的输入,然后展示如何从 Zipline 回测性能的 DataFrame 中提取它们。本节的代码示例在笔记本 03_pyfolio_demo.ipynb 中。
从 Alphalens 获取 pyfolio 输入
Pyfolio 还直接集成了 Alphalens,并允许使用 create_pyfolio_input 创建 pyfolio 输入数据:
from alphalens.performance import create_pyfolio_input
qmin, qmax = factor_data.factor_quantile.min(),
factor_data.factor_quantile.max()
input_data = create_pyfolio_input(alphalens_data,
period='1D',
capital=100000,
long_short=False,
equal_weight=False,
quantiles=[1, 5],
benchmark_period='1D')
returns, positions, benchmark = input_data
有两个选项可以指定如何生成投资组合权重:
-
long_short: 如果为False,权重将对应于因子值除以它们的绝对值,以便负因子值生成空头头寸。如果为True,则因子值首先被减去,以便多头和空头头寸相互抵消,投资组合是市场中性的。 -
equal_weight: 如果True并且long_short为True,资产将分成两个等大小的组,顶部/底部一半组成多头/空头头寸。
如果factor_data 包括每个资产的部门信息,还可以为组创建多头-空头投资组合,例如。
从 Zipline 回测获取 pyfolio 输入
还可以使用 extract_rets_pos_txn_from_zipline 将 Zipline 回测的结果转换为所需的 pyfolio 输入:
returns, positions, transactions =
extract_rets_pos_txn_from_zipline(backtest)
步行前测试 - 样本外回报
测试交易策略涉及回测和前瞻性测试。前者涉及历史数据,并且通常指的是用于微调 alpha 因子参数的样本期间。前瞻性测试在新市场数据上模拟策略,以验证其在样本外的表现,并且不会过于贴近特定的历史情况。
Pyfolio 允许指定一个样本外期间来模拟前瞻性测试。测试策略以获得统计上可靠的结果时,有许多方面需要考虑。我们将在第八章 ML4T 工作流程 – 从模型到策略回测 中更详细地讨论这一点。
函数 plot_rolling_returns 显示样本内和样本外累积回报与用户定义的基准(我们使用标准普尔 500)的对比。Pyfolio 将累积回报计算为每个回报加 1 后的简单回报的乘积:
from pyfolio.plotting import plot_rolling_returns
plot_rolling_returns(returns=returns,
factor_returns=benchmark_rets,
live_start_date='2016-01-01',
cone_std=(1.0, 1.5, 2.0))
图 5.4 中的图表包括一个锥体,显示扩展的置信区间,以指示在随机行走假设下,样本外回报何时似乎不太可能。在这里,我们的玩具策略在模拟的 2016 年样本外期间表现不佳,相对于标准普尔 500 指数:
图 5.4:Pyfolio 累积表现图
摘要性能统计
Pyfolio 提供了几个分析函数和图表。perf_stats 摘要显示了年度和累积回报、波动率、偏度和峰度以及 SR。
以下附加指标(也可以单独计算)最为重要:
-
最大回撤:相对于前一峰值的最大百分比损失
-
卡尔玛比率:年度组合回报与最大回撤的相对值
-
欧米伽比率:对于回报目标的基于概率加权的增益与损失比率,默认为零
-
索蒂诺比率:相对于下行标准差的超额回报
-
尾部比率:右尾部的大小(增益,第 95 百分位的绝对值)相对于左尾部的大小(损失,第 5 百分位的绝对值)
-
每日风险价值(VaR):对应于每日均值下两个标准偏差的损失
-
阿尔法:未被基准回报解释的投资组合回报
-
贝塔:与基准的暴露
函数 plot_perf_stats 通过自助法估计参数变异性,并将结果显示为箱线图:
图 5.5:Pyfolio 表现统计图
函数 show_perf_stats 计算整个期间以及样本内和样本外期间的多个指标:
from pyfolio.timeseries import show_perf_stats
show_perf_stats(returns=returns,
factor_returns=benchmark_rets,
positions=positions,
transactions=transactions,
live_start_date=oos_date)
对于从 MeanReversion 因子派生的模拟多空投资组合,我们得到以下表现统计:
| 指标 | 全部 | 样本内 | 样本外 |
|---|---|---|---|
| 年度回报 | 2.80% | 2.10% | 4.70% |
| 累积回报 | 11.60% | 6.60% | 4.70% |
| 年度波动率 | 8.50% | 8.80% | 7.60% |
| 夏普比率 | 0.37 | 0.29 | 0.64 |
| 卡尔马比率 | 0.21 | 0.16 | 0.57 |
| 稳定性 | 0.26 | 0.01 | 0.67 |
| 最大回撤 | -13.10% | -13.10% | -8.30% |
| 欧米伽比率 | 1.07 | 1.06 | 1.11 |
| 索提诺比率 | 0.54 | 0.42 | 0.96 |
| 偏度 | 0.33 | 0.35 | 0.25 |
| 峰度 | 7.2 | 8.04 | 2 |
| 尾部比率 | 1.04 | 1.06 | 1.01 |
| 日风险价值 | -1.10% | -1.10% | -0.90% |
| 总杠杆 | 0.69 | 0.68 | 0.72 |
| 日换手率 | 8.10% | 8.00% | 8.40% |
| Alpha | 0 | -0.01 | 0.03 |
| Beta | 0.25 | 0.27 | 0.17 |
请参阅附录以获取有关投资组合风险和回报度量的计算和解释详细信息。
跌幅期和因素暴露
函数plot_drawdown_periods(returns)绘制投资组合的主要跌幅期,并且还有几个绘图函数显示滚动的 SR 和滚动因子暴露于市场 beta 或 Fama-French 尺寸、增长和动量因子:
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(16, 10))
axes = ax.flatten()
plot_drawdown_periods(returns=returns, ax=axes[0])
plot_rolling_beta(returns=returns, factor_returns=benchmark_rets,
ax=axes[1])
plot_drawdown_underwater(returns=returns, ax=axes[2])
plot_rolling_sharpe(returns=returns)
图 5.6 中的图表突出显示了各种撕裂表中包含的可视化子集,说明了 pyfolio 如何让我们深入了解绩效特征并让我们接触到风险和回报的基本驱动因素:
图 5.6:随时间变化的各种 pyfolio 绘图
建模事件风险
Pyfolio 还包括各种事件的时间表,您可以使用它来比较投资组合在此期间与基准的表现。 Pyfolio 默认使用标准普尔 500 指数,但您也可以提供您选择的基准回报。以下示例将绩效与 2015 年秋季的标准普尔 500 指数相比,该指数跌至英国脱欧后的情况:
interesting_times = extract_interesting_date_ranges(returns=returns)
interesting_times['Fall2015'].to_frame('pf') \
.join(benchmark_rets) \
.add(1).cumprod().sub(1) \
.plot(lw=2, figsize=(14, 6), title='Post-Brexit Turmoil')
图 5.7显示了结果图:
图 5.7:Pyfolio 事件风险分析
摘要
在本章中,我们涵盖了投资组合管理的重要主题,这涉及将投资头寸组合起来以管理风险和回报的权衡目标。我们介绍了 pyfolio 来计算和可视化关键的风险和回报指标,并比较各种算法的性能。
我们看到准确的预测对于优化投资组合权重和最大化多样化效益至关重要。我们还探讨了机器学习如何通过从资产收益协方差矩阵中学习分层关系,促进更有效的投资组合构建。
现在我们将转向本书的第二部分,重点介绍机器学习模型的使用。这些模型将通过更有效地利用更多样化的信息来产生更准确的预测。他们这样做是为了捕捉比迄今为止最突出的简单的 alpha 因子更复杂的模式。
我们将首先通过交叉验证来训练、测试和调整用于回归和分类的线性模型,以实现强健的样本外表现。我们还将把这些模型嵌入到定义和回测算法交易策略的框架中,这是我们在前两章中介绍过的。
第六章:机器学习过程
本章开始了本书的第二部分,我们将演示如何使用各种监督和无监督的机器学习模型进行交易。在展示相关 Python 库的各种应用之前,我们将解释每个模型的假设和用例。在第 2-4 部分我们将涵盖的模型类别包括:
-
用于截面、时间序列和面板数据回归和分类的线性模型
-
广义加性模型,包括非线性基于树的模型,如决策树
-
集成模型,包括随机森林和梯度提升机
-
用于降维和聚类的无监督线性和非线性方法
-
神经网络模型,包括循环和卷积架构
-
强化学习模型
我们将这些模型应用于本书第一部分介绍的市场、基本和替代数据源。我们将通过演示如何将这些模型嵌入到将模型信号转换为交易的交易策略中,如何优化投资组合以及如何评估策略绩效来扩展到目前为止所涵盖的材料。
这些模型及其应用中有许多共同点。本章涵盖了这些共同点,以便我们可以专注于以下章节中的特定模型用法。它们包括从数据中学习功能关系的总体目标,通过优化目标或损失函数。它们还包括测量模型性能的相关方法。
我们将区分无监督学习和监督学习,并概述算法交易的用例。我们将对比监督回归和分类问题,并使用监督学习进行输入和输出数据之间关系的统计推断,以及用于未来输出预测的使用。
我们还将说明预测误差是由于模型的偏差或方差,或者是由于数据中高噪声信号比引起的。最重要的是,我们将提出诊断错误来源(如过度拟合)并改善模型性能的方法。
在本章中,我们将涵盖以下与在实践中应用 ML 工作流相关的主题:
-
从数据中学习的监督和无监督学习的工作原理
-
训练和评估用于回归和分类任务的监督学习模型
-
偏差-方差权衡如何影响预测性能
-
如何诊断和解决由于过度拟合而导致的预测误差
-
使用交叉验证来优化超参数,重点关注时间序列数据
-
在测试样本外时,为什么金融数据需要额外注意
如果您已经对机器学习非常熟悉,可以跳过并直接开始学习如何使用机器学习模型为算法交易策略生成和组合阿尔法因子。本章的 GitHub 存储库中包含代码示例和额外资源的目录。
机器学习是如何从数据中进行的
许多机器学习的定义都围绕着对数据中有意义的模式的自动检测。两个显著的例子包括:
-
人工智能先驱阿瑟·塞缪尔森在 1959 年将机器学习定义为计算机科学的一个子领域,使计算机能够在没有明确编程的情况下学习。
-
汤姆·米切尔,这个领域的现任领导者之一,更加具体地确定了一个明确定义的学习问题,1998 年:一台计算机程序通过与任务和性能度量相关的经验来学习,以确定任务的性能是否随着经验的积累而提高(Mitchell 1997)。
经验以训练数据的形式呈现给算法。与以往构建解决问题的机器的尝试的主要区别在于,算法用于做出决策的规则是从数据中学习的,而不是像上世纪 80 年代突出的专家系统那样由人类编程的。
推荐的涵盖各种算法和通用应用的教科书包括 James 等人(2013),Hastie、Tibshirani 和 Friedman(2009),Bishop(2006)和 Mitchell(1997)。
挑战——将算法与任务匹配
自动学习的关键挑战是识别训练数据中的模式,这些模式在将模型的学习推广到新数据时具有意义。模型能够识别的潜在模式的数量很大,而训练数据只构成了算法在未来执行任务时可能遇到的更大现象集合的样本。
无限数量的函数可能会从给定输入生成观察到的输出,这使得搜索真实函数的过程成为不可能,除非限制符合条件的候选集。算法能够学习的模式类型受其包含可能表示的函数的假设空间大小以及样本数据提供的信息量的限制。
假设空间的大小在各种算法之间变化很大,我们将在接下来的章节中看到。一方面,这种限制使得成功搜索成为可能,另一方面,它意味着一种归纳偏差,可能导致算法从训练样本泛化到新数据时性能不佳。
因此,关键挑战在于如何选择一个具有足够大的假设空间的模型,以包含对学习问题的解决方案,同时又足够小,以确保给定训练数据的可靠学习和概括性。有了更多信息的数据,具有更大假设空间的模型成功的机会更大。
无免费午餐定理表明没有通用的学习算法。相反,学习者的假设空间必须根据任务领域的先验知识进行定制,以便搜索出能够成功概括的有意义模式(Gómez and Rojas 2015)。
我们将在本章中密切关注模型对特定任务的数据关系所作的假设,并强调将这些假设与从数据探索中获得的经验证据相匹配的重要性。
机器学习任务有几个类别,其目的、可用信息以及因此学习过程本身有所不同。主要类别包括监督、无监督和强化学习,接下来我们将审查它们的关键区别。
监督学习——通过示例教学
监督学习是最常用的 ML 类型。我们将在本书的大部分章节中致力于这一类别的应用。术语监督意味着存在一个结果变量来引导学习过程——也就是说,它教会了算法对手头任务的正确解决方案。监督学习旨在从反映这种关系的个体样本中捕获功能输入-输出关系,并通过对新数据做出有效的陈述来应用其学习。
根据领域的不同,输出变量也可以互换地称为标签、目标或结果,以及内生或左侧变量。我们将使用y[i]表示结果观测值i=1,...,N,或者y表示(列)向量的结果。一些任务具有多个结果,并称为多标签问题。
监督学习问题的输入数据也称为特征,以及外生或右侧变量。我们使用x[i]表示观测值i=1,...,N的特征向量,或者在矩阵表示中表示为X,其中每列包含一个特征,每行包含一个观测值。
监督学习问题的解决方案是一个函数 ,表示模型从样本中学到的输入-输出关系,并逼近真实关系,表示为
。这个函数可以潜在地用于推断变量之间的统计关联或甚至因果关系,超出样本范围,或者可以用于预测新输入数据的输出。
从数据中学习输入-输出关系以便对新输入进行准确预测的任务面临重要的权衡。更复杂的模型具有更多的可移动部分,能够表示更微妙的关系。然而,它们也更有可能学习到特定于训练样本的随机噪声,而不是代表一般模式的系统信号。当这种情况发生时,我们称模型对训练数据过度拟合。此外,复杂模型可能也更难以检查,这使得理解学到的关系的性质或特定预测的驱动因素变得更加困难。
另一方面,过于简单的模型将错过复杂的信号并提供有偏见的结果。这种权衡在监督学习中被称为偏差-方差权衡,但从概念上讲,这也适用于其他形式的机器学习,即太简单或太复杂的模型可能在训练数据之外表现不佳。
无监督学习-发现有用的模式
当解决无监督学习问题时,我们只观察特征,并没有对结果进行测量。 无监督算法的目标不是预测未来结果或推断变量之间的关系,而是旨在识别输入中的结构,从而允许对数据中包含的信息进行新的表示。
经常,成功的衡量标准是结果对解决其他问题的贡献。这包括识别观察之间的共同点或群集,或者转换特征以获得捕获相关信息的压缩摘要。
关键挑战在于无监督算法必须在没有结果信息提供的情况下完成任务。因此,我们通常无法像在监督情况下那样将结果与基本事实进行评估,其质量可能视人而异。但是,有时,我们可以评估其对下游任务的贡献,例如降维使得更好的预测成为可能。
有许多方法,从成熟的聚类算法到前沿的深度学习模型,以及几个与我们目的相关的使用案例。
使用案例-从风险管理到文本处理
在后续章节中,我们将涵盖许多无监督学习的交易使用案例:
-
将具有相似风险和回报特征的证券分组(请参阅第十三章,使用无监督学习的数据驱动风险因子和资产配置中的分层风险均等化)
-
使用主成分分析(第十三章,使用无监督学习的数据驱动风险因子和资产配置)或自编码器(第十九章,用于多变量时间序列和情感分析的 RNN)找出驱动大量证券表现的少量风险因子。
-
识别文档集合(例如,收入电话会议记录)中包含的最重要方面的潜在主题(第十四章,用于交易的文本数据 - 情感分析)
在高层次上,这些应用依赖于识别聚类的方法和降低数据维度的方法。
聚类算法 - 寻找相似观察
聚类算法应用相似性概念来识别包含可比较信息的观察或数据属性。它们通过将大量数据点分配给较少数量的聚类来总结数据集。它们这样做是为了使聚类成员彼此之间的关系比与其他聚类成员的关系更密切。
聚类算法在假设关于各种分组是如何生成以及是什么使它们相似方面有所不同。因此,它们倾向于产生不同类型的聚类,因此应根据数据的特性选择。一些著名的例子包括:
-
K 均值聚类:数据点属于等大小的k个椭圆形簇之一。
-
高斯混合模型:数据点由各种多元正态分布之一生成。
-
基于密度的聚类:聚类可以是任意形状的,并且仅由附近的最小数量的数据点的存在定义。
-
分层聚类:数据点属于由逐渐合并较小簇形成的各种超集。
降维 - 压缩信息
降维生成包含源数据中最重要信息的新数据。这些算法不是将数据分组到保留原始数据的簇中,而是以使用更少特征来表示原始信息的目标来转换数据。
算法在数据转换方式及因此产生的压缩数据集的性质上有所不同,如下列表所示:
-
主成分分析(PCA):找到线性转换,捕获现有数据集中大部分方差
-
流形学习:识别出产生数据的低维表示的非线性变换
-
自动编码器:使用神经网络对数据进行非线性压缩,最小化信息损失。
在以下几章中,我们将更深入地研究这些无监督学习模型,包括对自然语言处理(NLP)的重要应用,例如主题建模和 Word2vec 特征提取。
强化学习 - 通过试错学习
强化学习(RL)是 ML 的第三种类型。它以代理为中心,代理需要在每个时间步选择一个动作,这是基于环境提供的信息。代理可以是自动驾驶汽车、玩棋盘游戏或视频游戏的程序,或者在某个安全市场上运行的交易策略。您可以在*Sutton 和 Barto(2018)*中找到一篇优秀的介绍。
代理的目标是选择随着时间推移产生最高回报的行动,这是基于一组描述环境当前状态的观察而进行的。它既是动态的又是交互式的:积极和消极奖励的流动影响着算法的学习,而现在采取的行动可能会影响环境和未来的奖励。
代理需要从一开始就采取行动,并以“在线”方式学习,随着时间的推移,一次一个样本。学习过程采用试错方法。这是因为代理需要在利用过去产生某种奖励的行动和探索可能增加未来奖励的新行动之间进行权衡。RL 算法使用动态系统理论以及具有不完整信息的马尔可夫决策过程的最优控制来优化代理的学习。
RL 与监督学习不同,监督学习的训练数据为算法提供了上下文和正确决策。它专门针对交互式设置,其中结果只在一段时间后变得可用,并且学习必须随着代理获取新经验而持续进行。
人工智能(AI)中一些最显著的进展涉及强化学习(RL),它使用深度学习来逼近动作、环境和未来奖励之间的功能关系。它与无监督学习不同,因为尽管有延迟,但会提供对动作的反馈。
RL 特别适用于算法交易,因为在不确定的、动态的环境中,追求回报的代理模型与与金融市场互动的投资者或交易策略有很多共同之处。我们将在第二十一章中介绍 RL 方法来构建算法交易策略,生成对抗网络用于合成时间序列数据。
机器学习工作流程
为了最大程度地提高成功的机会并节约资源,为算法交易策略开发 ML 解决方案需要系统的方法。使过程透明且可复制非常重要,以促进协作、维护和后续的改进。
以下图表概述了从问题定义到部署预测解决方案的关键步骤:
图 6.1:机器学习工作流程的关键步骤
整个过程都是迭代的,并且各个阶段所需的工作量会根据项目而变化。然而,通常情况下,这个过程应该包括以下步骤:
-
确定问题,确定目标指标,并定义成功。
-
数据源,清理和验证数据。
-
了解你的数据并生成信息性特征。
-
为你的数据选择一个或多个适合的机器学习算法。
-
训练,测试和调整你的模型。
-
使用你的模型来解决原始问题。
我们将在以下部分逐步完成这些步骤,使用一个简单的示例来说明一些关键点。
基本步骤 - 最近邻算法
本书 GitHub 仓库中本章文件夹中的 machine_learning_workflow.ipynb 笔记本包含了几个示例,用于演示使用房价数据集的机器学习工作流程。
我们将使用相当直观的 最近邻 (KNN) 算法,它允许我们解决回归和分类问题。在其默认的 scikit-learn 实现中,它识别出最近的 k 个数据点(基于欧氏距离)来进行预测。在分类或回归情况下,它分别预测邻居中的最频繁类别或平均结果。
这一章的 GitHub 上的 README 链接到了额外的资源;查看 Bhatia 和 Vandana (2010) 进行简要调查。
框架问题 - 从目标到度量
任何机器学习项目的起点是它最终旨在解决的用例。有时,这个目标将是统计推断,以识别变量之间的关联或甚至因果关系。然而,最常见的情况是,目标是预测结果以产生交易信号。
推断和预测任务都依赖于度量指标来评估模型实现其目标的程度。由于它们在实践中的显著性,我们将重点关注常见的目标函数以及用于预测模型的相应误差度量。
我们通过输出的性质来区分预测任务:连续输出变量构成了一个 回归 问题,一个分类变量意味着 分类,而有序分类变量的特殊情况代表了一个 排名 问题。
通常可以以不同的方式对给定的问题进行框架化。手头的任务可能是如何有效地结合几个 alpha 因子。你可以将这个任务框架化为一个回归问题,旨在预测回报,一个二元分类问题,旨在预测未来价格走势的方向,或一个多类问题,旨在将股票分配到各种表现类别,比如回报五分位数。
在下一节中,我们将介绍这些目标,并看看如何测量和解释相关的错误指标。
预测与推断
监督学习算法产生的功能关系可用于推断,即了解结果生成的方式。或者,您可以用它来预测未知输入的输出。
对于算法交易,我们可以使用推断来估计资产回报与风险因子的统计关联。这意味着,例如,评估此观察是否可能由噪声引起,而不是风险因子的实际影响。反过来,预测可以用来预测风险因子,这可以帮助预测资产回报和价格,并转化为交易信号。
统计推断是关于从样本数据中得出关于潜在概率分布或总体参数的结论。可能的结论包括关于个体变量分布特征的假设检验,或者关于变量之间的数值关系的存在或强度的假设检验。它们还包括指标的点估计或区间估计。
推断取决于关于生成数据的过程的假设。我们将回顾这些假设以及在线性模型中用于推断的工具,在那里它们已经得到了很好的确认。更复杂的模型对输入和输出之间的结构关系做出更少的假设。相反,它们以较少的限制处理函数逼近任务,同时将数据生成过程视为黑匣子。
这些模型,包括决策树、集成模型和神经网络,因其在预测任务上的表现通常优于其他模型而备受青睐。然而,我们将看到,近年来已经有大量努力增加复杂模型的透明度。例如,随机森林最近获得了一个用于统计推断的框架(Wager 和 Athey 2019)。
因果推断 - 相关性不意味着因果关系
因果推断旨在识别输入值导致特定输出的关系 - 例如,一定的宏观变量组合导致给定资产价格以某种方式变动,同时假设所有其他变量保持不变。
关于两个或更多变量之间的关系的统计推断会产生相关性的度量。只有在满足其他几个条件时,相关性才能被解释为因果关系,例如当排除了替代解释或逆向因果关系时。
满足这些条件需要一个实验设置,在这个设置中,所有感兴趣的变量可以完全控制,以隔离因果关系。或者,准实验设置以随机方式将观察单位暴露于输入变化中,以排除其他可观察或不可观察的特征对环境变化观察效果的影响。
这些条件很少被满足,因此推断性结论需要谨慎处理。对于依赖于特征和输出之间的统计关联的预测模型的性能也是如此,这种关联可能会随着不是模型一部分的其他因素的变化而变化。
KNN 模型的非参数性质不利于推断,因此我们将推迟工作流程中的此步骤,直到我们在第七章,线性模型 - 从风险因素到回报预测中遇到线性模型。
回归 - 流行的损失函数和误差度量
回归问题旨在预测连续变量。均方根误差(RMSE)是最受欢迎的损失函数和误差度量,其中一个原因是它是可微的。损失是对称的,但更大的错误在计算中占更多的权重。使用平方根的优点是我们可以用目标变量的单位来衡量误差。
误差的对数的均方根(RMSLE)适用于目标受指数增长的情况。其不对称的惩罚使得负误差比正误差的权重小。您也可以在训练模型之前对目标进行对数转换,然后使用 RMSE,就像我们将在本节后面的示例中所做的那样。
绝对误差的平均值(MAE)和绝对误差的中位数(MedAE)是对称的,但不会给较大的错误更多的权重。MedAE 对异常值具有鲁棒性。
解释方差得分计算模型解释的目标方差比例,介于 0 和 1 之间。R2 得分也称为决定系数,如果残差的均值为 0,则产生相同的结果,否则可能会有所不同。特别是在对样本外数据进行计算时(或者对没有截距的线性回归进行计算时),它可能为负。
下表定义了用于计算的公式以及可以从度量模块导入的相应的 scikit-learn 函数。scoring参数与自动化的训练-测试函数(如cross_val_score和GridSearchCV)结合使用,我们稍后将在本节中介绍,并在附带的笔记本中进行说明:
| 名称 | 公式 | scikit-learn 函数 | 评分参数 |
|---|---|---|---|
| 均方误差 | mean_squared_error | neg_mean_squared_error | |
| 平均平方对数误差 | mean_squared_log_error | neg_mean_squared_log_error | |
| 平均绝对误差 | mean_absolute_error | neg_mean_absolute_error | |
| 中位绝对误差 | median_absolute_error | neg_median_absolute_error | |
| 解释方差 | explained_variance_score | explained_variance | |
| R²得分 | r2_score | r2 |
图 6.2 展示了我们将在笔记本中计算的房价回归的各种错误度量:
图 6.2:样本内回归误差
sklearn 函数还支持多标签评估——即,将多个结果值分配给单个观测值;有关更多详细信息,请参阅 GitHub 上引用的文档。
分类 – 理解混淆矩阵的含义
分类问题具有分类结果变量。大多数预测器将输出一个分数,以指示观察是否属于某一类别。在第二步中,这些分数然后被转换为实际预测,使用一个阈值值。
在二元情况下,具有正类标签和负类标签,得分通常在零和一之间变化,或者相应地进行归一化。一旦将得分转换为对其中一类的预测,就会产生四种结果,因为两个类中的每一个都可以被正确或错误地预测。如果区分几种潜在的错误,那么在两个以上的类别中可能会有更多的情况。
所有错误度量都是从 2×2 混淆矩阵的四个字段的预测分解中计算的,该矩阵将实际类别和预测类别相关联。
下表中列出的度量,如准确性,评估了给定阈值下的模型:
图 6.3:混淆矩阵和相关的错误度量
分类器通常不会输出校准的概率。相反,用于区分正负情况的阈值本身是一个决策变量,应该进行优化,考虑到正确和错误预测的成本和效益。
一切相等的情况下,较低的阈值往往意味着更多的正预测,可能会导致假阳性率上升,而对于较高的阈值,相反的可能是真的。
接收器操作特性曲线下的面积
接收器操作特性 (ROC) 曲线允许我们根据它们的性能可视化、比较和选择分类器。它计算了使用所有预测分数作为阈值产生类预测的真正阳性率 (TPR) 和假阳性率 (FPR) 的对。它在一个边长为单位的正方形内可视化这些对。
随机预测(根据类别不平衡加权),平均而言,产生相等的 TPR 和 FPR,这些都出现在对角线上,这成为基准案例。由于性能不佳的分类器会从重新标记预测中受益,因此该基准也成为最小值。
曲线下面积(AUC)被定义为 ROC 图下的面积,其值在 0.5 和最大值 1 之间变化。它是分类器分数能够根据其类别成员资格对数据点进行排名的摘要度量。更具体地说,分类器的 AUC 具有重要的统计属性,表示分类器将随机选择的正实例排在随机选择的负实例之上的概率,这相当于 Wilcoxon 排名检验(Fawcett 2006)。此外,AUC 具有不对类别不平衡敏感的好处。
精确率-召回率曲线 - 放大一个类
当对其中一个类的预测特别感兴趣时,精确率和召回率曲线可视化了这些误差指标在不同阈值下的权衡。这两个指标评估了特定类别的预测质量。以下列表显示了它们如何应用于正类别:
-
召回率(Recall)衡量分类器预测为正例的实际正类成员所占比例,针对给定的阈值。它源自信息检索,并且衡量了搜索算法成功识别的相关文档的比例。
-
精确率(Precision),相反地,衡量了正确的正预测所占的比例。
召回率通常随着较低的阈值而增加,但精确率可能会降低。精确率-召回率曲线可视化了可达到的组合,并允许根据错过大量相关情况或产生质量较低的预测的成本和收益来优化阈值。
F1 分数是给定阈值下精确率和召回率的调和平均数,并且可以用于数值优化阈值,同时考虑到这两个指标应该承担的相对权重。
图 6.4 展示了 ROC 曲线及相应的 AUC,以及精确率-召回率曲线和 F1 分数,使用精确率和召回率的相等权重,得出了 0.37 的最佳阈值。该图表摘自附带的笔记本,您可以在其中找到针对二值化房价操作的 KNN 分类器的代码:
图 6.4:接收器操作特征(ROC)、精确率-召回率曲线和 F1 分数图
收集和准备数据
我们已经在第二章,市场和基础数据 - 来源和技术和第三章,金融替代数据 - 类别和用例中讨论了如何获取市场、基础和替代数据的重要方面。我们将继续使用这些来源的各种示例,以展示各种模型的应用。
除了市场和基本数据外,我们还将获取和转换文本数据,当我们探索自然语言处理时,获取图像数据并进行图像处理和识别时。除了获取、清洗和验证数据外,我们还可能需要分配标签,如新闻文章的情感或时间戳,以使其与通常以时间序列格式可用的交易数据对齐。
将其存储在能够快速探索和迭代的格式中也很重要。我们推荐 HDF 和 parquet 格式(参见第二章,市场和基本数据-来源和技术)。对于不适合内存且需要在多台机器上进行分布式处理的数据,Apache Spark 通常是交互式分析和机器学习的最佳解决方案。
探索、提取和工程化特征
理解单个变量的分布以及结果和特征之间的关系是选择适当算法的基础。这通常从诸如散点图的可视化开始,如附带的笔记本中所示并在图 6.5中显示:
图 6.5:结果和特征的成对散点图
它还包括从线性指标如相关性到非线性统计量如我们在第四章中介绍的信息系数时遇到的 Spearman 等级相关系数的数值评估。还有信息论量度,如互信息,我们将在下一小节中进行说明。
系统性的探索性分析也是成功预测模型中往往最重要的一个组成部分的基础:特征工程,它提取了数据中包含的信息,但这些信息在原始形式下不一定对算法可见。特征工程受益于领域专业知识、统计学和信息论的应用,以及创造力。
它依赖于智能数据转换,可以有效地揭示输入和输出数据之间的系统关系。有许多选择,包括异常值检测和处理、功能转换以及多个变量的组合,包括无监督学习。我们将在整个过程中举例说明,但会强调这个 ML 工作流程的核心方面最好通过经验来学习。Kaggle 是一个与社区分享经验的其他数据科学家的好地方。
使用信息论评估特征
特征和结果之间的互信息(MI)是两个变量之间相互依赖的一种度量。它将相关性的概念扩展到非线性关系。更具体地说,它量化了通过另一个随机变量获得的有关某一随机变量的信息。
互信息(MI)的概念与随机变量的熵的基本概念密切相关。熵量化了随机变量中包含的信息量。形式上,两个随机变量 X 和 Y 的互信息——I(X, Y)——定义如下:
sklearn 函数实现了feature_selection.mutual_info_regression,它计算所有特征和连续结果之间的互信息,以选择最有可能包含预测信息的特征。还有一个分类版本(有关更多详情,请参阅 sklearn 文档)。mutual_information.ipynb笔记本包含了我们在第四章,金融特征工程—如何研究 Alpha 因子中创建的财务数据的应用。
选择一个 ML 算法
本书的其余部分将介绍几个模型家族,从对输入和输出变量之间的功能关系的本质作出相当强的假设的线性模型,到几乎没有假设的深度神经网络。正如在介绍部分中所提到的,更少的假设将需要更多关于关系的重要信息,以便学习过程能够成功进行。
在介绍这些模型时,我们将概述关键假设及其如何进行检验。
设计和调整模型
ML 过程包括诊断和管理模型复杂性的步骤,基于对模型的泛化误差的估计。ML 过程的一个重要目标是使用统计上可靠且高效的程序获取对此错误的无偏估计。管理模型设计和调整过程的关键是理解偏差-方差权衡与欠拟合和过拟合之间的关系。
偏差-方差权衡
ML 模型的预测误差可以分解为可减少和不可减少的部分。不可减少部分是由于数据中的随机变动(噪声)引起的,例如,由于缺乏相关变量、自然变动或测量误差。广义泛化误差的可减少部分又可分解为由偏差和方差引起的误差。
这两者都是由于真实功能关系与机器学习算法所做假设之间的差异而产生的,详细列在以下清单中:
-
偏差导致的误差:假设过于简单,无法捕捉真实功能关系的复杂性。因此,每当模型试图学习真实功能时,它都会出现系统性错误,平均而言,预测也将同样存在偏差。这也被称为欠拟合。
-
方差导致的错误:该算法在真实关系方面过于复杂。它不是捕捉真实关系,而是对数据过拟合,并从噪声中提取模式。因此,它从每个样本中学习到不同的功能关系,并且样本外的预测会变化很大。
欠拟合与过拟合 – 一个可视化例子
图 6.6 通过逐渐复杂的多项式逼近 正弦 函数来说明过拟合。更具体地说,我们抽取一个带有一些噪声的随机样本(n = 30)来学习不同复杂度的多项式(请参见笔记本中的代码 bias_variance.ipynb)。该模型预测新的数据点,我们捕获这些预测的均方误差。
图 6.6 的左侧面板显示了一次多项式;一条直线明显地欠拟合了真实函数。然而,估计线不会在从真实函数绘制的一个样本到下一个样本之间有明显差异。
中间面板显示了一个 5 次多项式在大约从 到
的区间上合理地逼近真实关系。另一方面,15 次多项式几乎完美地拟合了小样本,但提供了真实关系的不良估计:它对样本数据点的随机变化进行了过拟合,学到的函数将随样本的函数强烈变化:
图 6.6:多项式过拟合的可视化示例
如何处理偏差-方差折衷
为了进一步说明过拟合与欠拟合的影响,我们将尝试学习带有一定噪声的 正弦 函数的第九次泰勒级数逼近。图 6.7 显示了对真实函数的 100 个随机样本进行了欠拟合、过拟合和提供大约正确的灵活性的多项式的样本内外误差和样本外预测,分别为 1、15 和 9 次多项式。
左侧面板显示了从预测值中减去真实函数值产生的误差的分布。拟合度低但方差小的一次拟合多项式与拟合度高但方差极高的 15 次过拟合多项式的误差相比。欠拟合多项式产生一条直线,其内部拟合较差,样本外明显偏离目标。过拟合模型在样本内显示出最佳拟合,误差最小,但代价是样本外的大方差。与真实模型功能形式相匹配的适当模型在样本外数据上平均表现最佳。
图 6.7 的右侧面板显示实际预测而不是错误,以可视化实践中的不同拟合类型:
图 6.7:不同次数多项式的误差和样本外预测
学习曲线
学习曲线绘制了用于学习函数关系的数据集大小演变对训练和测试错误的影响。它有助于诊断给定模型的偏差-方差权衡,并回答增加样本量是否可能提高预测性能的问题。具有高偏差的模型将在样本内和样本外都具有高但相似的训练误差。过拟合模型将具有非常低的训练但较高的测试误差。
图 6.8显示了过拟合模型的样本外误差随着样本大小增加而下降,表明它可能受益于额外的数据或限制模型复杂性的工具,例如正则化。正则化向模型的复杂性添加了数据驱动的约束;我们将在第七章,线性模型 - 从风险因素到回报预测中介绍这个技术。
相反,拟合不足的模型需要更多特征或需要增加其容量以捕获真实关系:
图 6.8:学习曲线和偏差-方差权衡
如何使用交叉验证选择模型
通常有几个适用于您用例的候选模型,选择其中一个的任务称为模型选择问题。目标是识别在给定新数据时产生最低预测误差的模型。
一个好的选择需要对这种泛化误差进行无偏估计,而这又需要在不参与模型训练的数据上对模型进行测试。否则,模型将已经能够窥视“解决方案”,并提前了解有关预测任务的信息,这将夸大其性能。
为了避免这种情况,我们仅使用部分可用数据来训练模型,并将另一部分数据保留以验证其性能。模型在新数据上的预测误差的估计结果只有在绝对没有关于验证集的信息泄漏到训练集时才会无偏,如图 6.9所示:
图 6.9:训练集和测试集
交叉验证(CV)是一种常用的模型选择策略。CV 背后的主要思想是将数据分割一次或多次。这样做是为了每次分割都被用作一次验证集,而剩余部分被用作训练集:一部分数据(训练样本)用于训练算法,剩余部分(验证样本)用于估计算法的预测性能。然后,CV 选择具有最小估计误差或风险的算法。
可以使用几种方法来拆分可用数据。它们在使用用于训练的数据量、误差估计的方差、计算强度以及在拆分数据时是否考虑数据的结构性方面有所不同,例如维持类标签之间的比率。
虽然数据拆分启发式方法非常通用,但 CV 的一个关键假设是数据是独立同分布的(IID)。在接下来的部分和本书中的整个过程中,我们将强调时间序列数据需要不同的方法,因为它通常不符合这一假设。此外,我们需要确保拆分尊重时间顺序,以避免前瞻性偏差。我们将通过将一些信息从我们旨在预测的未来纳入历史训练集中来实现这一点。
模型选择通常涉及超参数调整,这可能导致许多 CV 迭代。表现最佳模型的结果验证分数将受到多重检验偏差的影响,它反映了 CV 过程中固有的抽样噪声。因此,它不再是泛化误差的良好估计。为了得到无偏的误差率估计,我们必须从新的数据集中估计分数。
因此,我们使用了数据的三分拆分,如 图 6.10 所示:其中一部分用于交叉验证,并被重复拆分为训练集和验证集。其余部分被保留为保留集,仅在交叉验证完成后使用一次,以生成一个无偏的测试误差估计。
我们将在下一章开始构建 ML 模型时说明这种方法:
图 6.10:训练、验证和保留测试集
如何在 Python 中实现交叉验证
我们将说明将数据拆分为训练集和测试集的各种选项。我们将通过展示如何将具有 10 个观测值的模拟数据集的索引分配给训练集和测试集(有关详细信息,请参见 cross_validation.py),如下代码所示:
data = list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Scikit-learn 的 CV 功能,我们将在本节中演示,可以从 sklearn.model_selection 导入。
对于将数据拆分为训练集和测试集的单次拆分,请使用 train_test_split,其中 shuffle 参数默认确保观察结果的随机选择。您可以通过设置 random_state 来对随机数生成器进行种子化以确保可复制性。还有一个 stratify 参数,它确保对于分类问题,训练集和测试集将包含大约相同比例的每个类别。结果如下所示:
train_test_split(data, train_size=.8)
[[8, 7, 4, 10, 1, 3, 5, 2], [6, 9]]
在这种情况下,我们使用除行号6和9之外的所有数据来训练模型,这些行号将用于生成预测并根据已知标签测量错误。该方法对于快速评估很有用,但对分割敏感,并且性能度量估计的标准误差会更高。
KFold 迭代器
KFold迭代器生成多个不相交的分割,并将这些分割中的每一个一次分配给验证集,如下所示的代码所示:
kf = KFold(n_splits=5)
for train, validate in kf.split(data):
print(train, validate)
[2 3 4 5 6 7 8 9] [0 1]
[0 1 4 5 6 7 8 9] [2 3]
[0 1 2 3 6 7 8 9] [4 5]
[0 1 2 3 4 5 8 9] [6 7]
[0 1 2 3 4 5 6 7] [8 9]
除了分割的数量外,大多数 CV 对象都接受一个shuffle参数,以确保随机化。为了使结果可重现,请按以下方式设置random_state:
kf = KFold(n_splits=5, shuffle=True, random_state=42)
for train, validate in kf.split(data):
print(train, validate)
[0 2 3 4 5 6 7 9] [1 8]
[1 2 3 4 6 7 8 9] [0 5]
[0 1 3 4 5 6 8 9] [2 7]
[0 1 2 3 5 6 7 8] [4 9]
[0 1 2 4 5 7 8 9] [3 6]
留一出交叉验证
原始 CV 实现使用一种留一法,将每个观测值一次用作验证集,如下所示的代码所示:
loo = LeaveOneOut()
for train, validate in loo.split(data):
print(train, validate)
[1 2 3 4 5 6 7 8 9] [0]
[0 2 3 4 5 6 7 8 9] [1]
...
[0 1 2 3 4 5 6 7 9] [8]
[0 1 2 3 4 5 6 7 8] [9]
这最大化了训练的模型数量,增加了计算成本。虽然验证集不重叠,但是训练集的重叠被最大化,推动模型和其预测误差的相关性增加。因此,具有更多折叠的模型的预测误差的方差更高。
留 P 个出交叉验证
与留一法 CV 类似的版本是留 P 个出 CV,它生成p数据行的所有可能组合,如下所示的代码所示:
lpo = LeavePOut(p=2)
for train, validate in lpo.split(data):
print(train, validate)
[2 3 4 5 6 7 8 9] [0 1]
[1 3 4 5 6 7 8 9] [0 2]
...
[0 1 2 3 4 5 6 8] [7 9]
[0 1 2 3 4 5 6 7] [8 9]
ShuffleSplit
ShuffleSplit类创建具有潜在重叠验证集的独立分割,如下所示的代码所示:
ss = ShuffleSplit(n_splits=3, test_size=2, random_state=42)
for train, validate in ss.split(data):
print(train, validate)
[4 9 1 6 7 3 0 5] [2 8]
[1 2 9 8 0 6 7 4] [3 5]
[8 4 5 1 0 6 9 7] [2 3]
跨金融领域的交叉验证挑战
到目前为止讨论的交叉验证方法的一个关键假设是训练样本的 IID 分布。
对于金融数据,情况通常并非如此。相反,由于串行相关性和时间变化的标准差,金融数据既不是独立分布的,也不是相同分布的,这也称为异方差性(有关更多详细信息,请参见第七章,线性模型 - 从风险因素到回报预测和第九章,用于波动率预测和统计套利的时间序列模型)。sklearn.model_selection模块中的TimeSeriesSplit旨在处理时间序列数据的线性顺序。
使用 scikit-learn 进行时间序列交叉验证
数据的时间序列性意味着交叉验证产生了一种情况,即未来的数据将用于预测过去的数据。这充其量是不现实的,最糟糕的情况是数据偷窥,因为未来的数据反映了过去的事件。
为了解决时间依赖性,TimeSeriesSplit对象实现了一种前向测试,其中扩展训练集,后续训练集是过去训练集的超集,如下所示的代码所示:
tscv = TimeSeriesSplit(n_splits=5)
for train, validate in tscv.split(data):
print(train, validate)
[0 1 2 3 4] [5]
[0 1 2 3 4 5] [6]
[0 1 2 3 4 5 6] [7]
[0 1 2 3 4 5 6 7] [8]
[0 1 2 3 4 5 6 7 8] [9]
您可以使用max_train_size参数来实现滚动交叉验证,其中训练集的大小随时间保持恒定,类似于 Zipline 测试交易算法。Scikit-learn 可以使用子类化设计自定义交叉验证方法,我们将在以下章节中实现这些方法。
净化、禁运和组合 CV
对于金融数据,标签通常是从重叠的数据点派生的,因为收益是从多个时期的价格计算出来的。在交易策略的背景下,模型的预测结果,可能意味着对某种资产采取头寸,只有在稍后评估此决策时才能知道,例如,当头寸被平仓时。
风险包括信息从测试到训练集的泄漏,这很可能会人为地增加性能。我们需要通过确保所有数据都是时间点数据来解决这一风险,即在用作模型输入时真正可用且已知。例如,财务披露可能涉及某个时间段,但仅在稍后才可用。如果我们过早包含这些信息,我们的模型可能在事后表现得比在实际情况下好得多。
Marcos Lopez de Prado,这个领域的领先实践者和学者之一,在他的著作《金融机器学习进展(2018)》中提出了几种方法来解决这些挑战。调整交叉验证以适应金融数据和交易背景的技术包括:
-
净化:消除训练数据点,其中评估发生在验证集中的时间点数据预测之后,以避免前瞻性偏差。
-
禁运:进一步消除跟随测试期的训练样本。
-
组合交叉验证:滚动交叉验证严重限制了可以测试的历史路径。相反,给定T个观测值,为N<T个组计算所有可能的训练/测试拆分,每个组都保持其顺序,并净化和禁运可能重叠的组。然后,在所有N-k组的组合上训练模型,同时在其余k组上测试模型。结果是可能的历史路径数量要大得多。
Prado 的金融机器学习进展中包含了实施这些方法的示例代码;该代码也可通过新的 Python 库 timeseriescv 获得。
使用 scikit-learn 和 Yellowbrick 进行参数调整
模型选择通常涉及重复交叉验证不同算法(如线性回归和随机森林)或不同配置的模型的样本外表现。不同的配置可能涉及超参数的更改或不同变量的包含或排除。
Yellowbrick 库扩展了 scikit-learn API 以生成诊断可视化工具,以便促进模型选择过程。这些工具可用于调查特征之间的关系,分析分类或回归错误,监视聚类算法性能,检查文本数据的特征,并帮助模型选择。我们将展示提供有价值信息的验证和学习曲线,在参数调整阶段使用 - 有关实施细节,请参阅machine_learning_workflow.ipynb笔记本。
验证曲线 - 绘制超参数的影响
验证曲线(见图 6.11中的左侧面板)可视化单个超参数对模型交叉验证性能的影响。这有助于确定模型是否对给定数据集欠拟合或过拟合。
在我们的KNeighborsRegressor示例中,只有一个超参数,即邻居的数量k。请注意,随着邻居数量的减少,模型的复杂度增加,因为模型现在可以为特征空间中更多不同的区域进行预测。
我们可以看到,在k大于 20 时,模型出现欠拟合现象。随着我们减少邻居数量并使我们的模型更复杂,验证误差下降。对于小于 20 的值,随着训练和验证误差的分歧和平均样本外表现的迅速恶化,模型开始过拟合:
图 6.11:验证和学习曲线
学习曲线 - 诊断偏差-方差权衡
学习曲线(见我们房价回归示例中图 6.11的右侧面板)有助于确定模型的交叉验证性能是否会受益于额外数据,并且预测误差更多是由偏差还是方差驱动。
如果训练和交叉验证分数收敛,那么增加数据很可能不会改善性能。在这一点上,重要的是评估模型性能是否符合人类基准的预期。如果不是这样,则应修改模型的超参数设置以更好地捕捉特征和结果之间的关系,或选择具有更高复杂度以捕捉复杂性的不同算法。
此外,由阴影置信区间显示的训练和测试误差的变化提供了有关预测误差的偏差和方差来源的线索。交叉验证误差的变异是方差的证据,而训练集的变异性则暗示了偏差,具体取决于训练误差的大小。
在我们的示例中,交叉验证性能持续下降,但增量改进已经减少,误差已经趋于稳定,因此增加训练集的好处不大。另一方面,与训练误差相比,数据显示了相当大的变化。
使用 GridSearchCV 和 pipeline 进行参数调整
由于超参数调整是机器学习工作流程的关键组成部分,因此有工具可以自动化此过程。Scikit-learn 库包括一个GridSearchCV接口,它并行地交叉验证所有参数组合,捕获结果,并在完整数据集上自动使用在交叉验证中表现最佳的参数设置训练模型。
在实践中,训练和验证集通常需要一些处理才能进行交叉验证。Scikit-learn 提供了Pipeline,可以在使用GridSearchCV时自动执行任何特征处理步骤。
您可以查看包含的machine_learning_workflow.ipynb笔记本中的实现示例,以了解这些工具的实际操作。
总结
在本章中,我们介绍了从数据中学习的挑战,并将监督学习、无监督学习和强化学习模型作为我们将在本书中研究的主要学习形式,以构建算法交易策略。我们讨论了监督学习算法需要对它们试图学习的功能关系进行假设的必要性。它们这样做是为了限制搜索空间,同时产生可能导致过度泛化误差的归纳偏差。
我们介绍了机器学习工作流程的关键方面,介绍了回归和分类模型的最常见错误度量标准,解释了偏差-方差权衡,并说明了使用交叉验证管理模型选择过程的各种工具。
在下一章中,我们将深入研究用于回归和分类的线性模型,以开发我们的第一个使用机器学习的算法交易策略。