模拟策略#

股票动量策略在概念上虽然简单,但在建模和模拟(Simulation)时却出奇地复杂。很容易遗漏关键细节或在过程中犯其他错误。技术在不断变化,所以我不会在这里讨论软件解决方案和数据提供商。这类内容更适合放在网站上,随着情况变化而更新信息。取而代之的是,本章旨在指出一些在模拟股票动量策略时可能非常重要的棘手概念。

如果你接受本书中的模拟结果,并且没有兴趣进行自己的模拟,你可以安全地跳过整章。

数据#

如果你输入垃圾,你输出的也只会是垃圾。你可以做出世界上最好的模拟,但如果一开始就没有获取正确的数据,那仍然是毫无用处的。在股票模拟中获取正确的数据是很棘手的。

首先,你需要确定你的数据范围(Data Universe)是什么。它不应该是一篮子你喜欢的股票。很可能你喜欢它们是因为它们表现良好,所以对它们进行模拟可能也会显示强劲的结果。这不是特别有用的信息。你需要的是一个逻辑上的市场集合——一个你很久以前就可能已经选定好的集合。

选择指数是解决这个问题的理性方法。交易指数成分股完全合理,而且是一种可以适当检验的基于规则的方法。在本书前面的模拟中,我使用了标普500指数。

找出当前哪些股票是标普500的成分股很容易,而且免费。问题当然在于,你不能假设你在过去十年中交易的是当前的标普500成分股。指数的构成在不断变化。

更糟糕的是,一只股票被纳入通常是因为它价格表现非常强劲。所以如果你使用当前的标普500成分股运行一个10年的动量股票模拟,结果看起来当然会很惊人。

你甚至可以说,标普500和大多数其他股票指数本身已经是动量策略了。

不,你需要的是将历史上的加入和退出股票都考虑在内。也就是说,在模拟中的任何一天,你的代码需要知道那天哪些股票是指数的成分股。这让事情变得有点棘手,不是吗?

我有没有提到这包括退市股票(Delisted Stocks)?是的,即使一只股票五年前破产了、被另一只股票合并了、或由于其他原因不再存在了,也必须包括在内。获取这些股票的数据可能有些麻烦,这绝对排除了所有免费数据源。

下一个重要问题是现金红利(Cash Dividends)。长期来看,这会产生非常显著的差异。一旦你找到了获取数据的方法,有两种方式处理红利数据。要么使用数据提供商计算的红利因子来创建总收益(Total Return)序列,要么直接处理现金注入。两种方法都能产生足够好的模拟结果。

如果你打算跳过任何这些部分,你的模拟工作将是浪费时间。你绝对需要历史成分股信息、退市股票数据和现金红利数据。这可能会变得昂贵和复杂,但这是无法绕过的。

要复制本书中的策略,你只需要日终数据。不需要任何日内时间序列,当然有也不会有坏处。如果你有日内数据,可以获取一些有趣的短期变动统计数据,但这不会改进策略本身。

真实投资组合模拟#

有几个好的模拟平台可用。但更有用的是大量完全无用的软件平台。后者通常更知名,用户群更广,价格也更低。

大多数面向散户交易社区的知名模拟平台都基于单一策略、单一工具。这种相当过时的概念假设你希望模拟单一市场上的一套规则。它基于寻找交易纳斯达克(NASDAQ)的完美规则集合这一古老幻想。如果你列出你能想到的前十个模拟程序,很可能它们都属于这一类。

单一策略、单一市场的软件解决方案对于任何专业用途来说都非常无用。忘记使用这类软件,转向至少能处理投资组合的产品吧。

下一个级别是为单一工具构建但后来被改造添加了某种投资组合功能的软件包。这类产品的好坏参差不齐。有些能用,其他的则是垃圾。据我所知和我的经验,没有一个是很出色的。

通常的问题在于它们处理数据的方式,或者更确切地说,处理数据的顺序。那些后来才被改造添加投资组合功能(而非从一开始就为此构建)的平台,往往以高度不切实际的方式处理数据。这可能会(而且往往会)导致奇怪的结果——用专业术语来说。

这类解决方案通常一次处理一个工具。如果你告诉它对一组工具进行模拟,它会先选择其中一个,遍历该工具的每一个数据点,然后再转到下一个。因此,如果你处理的是日线数据,它会先遍历工具1数据序列中的每一天,然后遍历工具2数据序列中的每一天,以此类推。

在以这种顺序遍历所有工具、检查买入和卖出点之后,常见的解决方案是再运行一次以确定仓位规模。

但在现实中,你不会先交易股票1的十年数据,然后交易股票2的十年数据。即使软件试图在这个过程中考虑投资组合的交互作用,这也不是一种现实的建模方式,很容易引发问题。尤其是它可能导致数据窥探(Data Snooping),即让你的代码窥见未来。

一个合适的模拟环境运作方式不同。它不是一次处理一个工具,而是按时间点处理。如果你处理的是日线数据,它应该同时查看所有工具在同一天的数据。你的代码不应该知道未来的任何信息,但它应该知道截至当前时点已经发生的所有事情。有编程背景的读者知道我在谈论编组(Marshaling)。不,我不会深入解释这个词,因为如果你不熟悉它,你很可能也不需要熟悉它。只要你理解按天逐日处理和按工具逐个处理之间的区别就行。

编程语言#

如果你想成为一名系统化交易者(Systematic Trader),你将不得不学习如何编程。这不是你可以外包的事情。工作的这一方面常常被轻描淡写。即使你是一个对冲基金量化团队的负责人,你仍然需要理解编程。

曾几何时,医生或律师不需要知道如何使用打字机。他们有别人为他们做这些事,他们太忙了,无暇从事如此卑微的活动。不幸的是,许多有抱负的交易者正是这样看待编程的。你能想象今天的律师不会打字吗?当然他有助理,但不会打字会让他完全无能为力。

好消息是你不需要学习很深的编程技能。对于想要更深入的人来说,深入可能有优势,但这不是必需的。然而,如果你想进入系统化交易领域,一些基本的编程技能是无法避免的。

当然,当我使用"基本"这个词时,我不是建议你学习如何在Basic语言中写Goto语句。选择合适的语言来构建你的策略可能非常重要。

大多数零售模拟平台使用简化的专有脚本语法。营销文案通常强调入门有多容易,即使是非编程人员。避免这类程序。它们极其有限,你无法用它们做任何有用的事情。

你需要的是灵活性。这是你在决定模拟环境时最重要的一点。速度不是大问题,除非你转向极低频率策略,而在那个领域,大型参与者已经拥有太大的优势。

你不会在嵌入许多零售平台的简化脚本语言中找到灵活性。但你会在行业标准编程语言中找到它。如果使用这类语言,你还会找到更多的支持、示例和资源。无论你想用C#、R、MatLab还是类似的语言编写代码,都是个人偏好和便利性的问题。然而,你应该瞄准一种真正的编程语言,避免脚本平台。这些简化的平台入门可能看起来快速简单,但当你意识到它们有多么受限时,你会很快后悔。

灵活性#

很少有模拟平台能做你需要的一切。可能没有平台能做到。然而,好的平台可以以某种方式实现。一个好的模拟平台足够灵活,可以以原始开发者没有预料到的方式进行扩展。平台越开放越好。作为策略开发者,你总会遇到希望你的平台能做更多事情的情况。这也是为什么行业标准语言如此重要的关键原因。

例如,可能没有模拟平台是专门为我们这种策略类型而设计的。毕竟,我们做的事情与大多数人模拟的相当不同。我们计算分析指标,对股票进行排名,基于排名选择股票,并根据股票自身的波动率和当前整体投资组合价值来确定仓位规模。这不是你日常所见的那种模拟。

我们可能还对特定历史日期的板块归因或投资组合配置等数据感兴趣。也许我们想分析行业风险和随时间变化的归因,或者正负异常值股票的影响。一个好的平台,经过一些工作,应该能够做到所有这些事情——只要它足够灵活。

多币种支持#

你可能已经注意到,我刻意将货币问题排除在此前的讨论之外。我们目前看到的策略仅应用于美国股票。这不是因为美国股票在动量策略上表现更好,也不仅仅是写一本关于美国股票的书比写瑞士股票的书更容易卖。嗯,不完全是。而是因为我假设大多数想要复制该策略的人缺乏访问多币种模拟环境的条件。

只处理单一货币计价的股票是一种奢侈。如果你曾经管理过全球投资组合,然后回到单一货币投资组合,会感觉像在度假。不再有货币敞口问题!

当你的股票范围覆盖多种货币时,你会面临一整套新的问题。有许多实际考量:是否需要对冲、如何对冲以及何时对冲;外汇买卖价差带来的额外成本;还有货币归因这个烦人的小问题。这在实际操作中开启了一整套新的头痛问题,即便在模拟阶段,也使得事情复杂得多。

当你模拟这类策略时,至关重要的是你的软件要了解股票的计价货币以及任何给定时间的汇率。货币波动将对你的表现产生非常大的影响。如果你计划对冲敞口,你还得建模那个对冲策略。这当然需要同时建模和模拟多个策略的能力。

或者你可以就坚守标普500指数股票,以简单的方式摆脱这个问题。

构建模拟结构#

在模拟中的每一天,你需要检查或计算几件事情。

首先,你需要检查股票是否符合纳入条件。在这种情况下,我们检查该股票在相关日期是否是指数成分股。一个方便的方法是构建一个自定义指标,使用参考数据在股票属于某指数时返回1,否则返回0。

然后我们需要计算该股票在过去90天内最大的跳空幅度。还记得我们有一条规则,不喜欢出现超过15%跳空的股票吗?计算过去90天内我们看到的最大跳空幅度,以备后续使用。

当然,我们需要一个粗略的波动率度量,而平均真实波幅(Average True Range, ATR)是一个不错的选择。在这个策略的基础版本中,我们使用20日ATR,所以将其纳入。

接下来是排名本身——即我们用来选择交易股票的分析指标。因此我们需要一个指示风险调整后动量的指标。如果你熟悉统计学,数学并不复杂。你需要首先计算基于90天的年化指数回归斜率,然后乘以决定系数(R²)。

排名方法在前文已有解释。

除了上述信息之外,我们还需要计算指数本身的200日移动均线,并确定其当前是高于还是低于指数本身。

一旦上述所有数据都计算完毕,我们就可以开始执行逻辑。首先需要做的是对股票进行排名。使用我们计算出的排名数值,列出当天所有符合条件的股票,并根据我们计算出的排名数值进行排序。

请注意,到目前为止的整个过程旨在复制我们在现实中会做的事情。我们在一天结束时计算所有相关分析指标,然后据此采取行动。

该逻辑每周只执行一次。偷懒的解决方案是使用周线数据运行模拟。更好的做法是检查应该在星期几执行逻辑。如果降到周线周期,你也会失去模拟结果的精细度,这是一个很大的损失。

如果今天是交易日,我们首先检查是否需要卖出任何东西。如果我们持有的股票离开了指数或由于其他原因停止交易,则退出。如果动量排名不再在前100名,则退出。如果股票交易价格低于其100日移动均线,则退出。如果它出现超过15%的跳空,则退出。

下一步是检查今天是否是再平衡日。如果是,我们需要逐一检查每个仓位,计算应该持有的大小,以及与当前持仓的差异。请记住规则以及我们如何确定仓位规模(如前文所述)。使用相同的公式重新计算规模,并向上或向下调整仓位大小以匹配。为了稍微减少交易,你可以设置一个过滤器,仅在差异大于5%时进行调整。

现在我们已经卖出了不再想要的股票,并再平衡了想要保留的股票,我们知道可用现金有多少。这时我们可以开始买入。

从排名列表的顶部开始。使用与之前相同的风险平价仓位规模,从顶部开始买入,直到现金用完。如果一只股票排名靠前且我们尚未持有,就买入它。

就是这样。

我是否听到有人请求源代码?嗯,那可能没有看起来那么有用。你最好理解逻辑步骤并自己构建它。我的源代码是用特定语言为特定环境编写的,对大多数读者来说可能本身并不太有用。

如果你真的想进入模拟领域,你面前有一些艰苦的工作。我非常鼓励你这样做,但你将不得不做一些繁重的工作。

凭借我在这里解释的逻辑,加上高质量的数据和合适的模拟环境,你将能够复制我的发现,并尝试自己的改进。这并不适合所有人,但通过本章,我想给每个足够感兴趣的人提供足够的信息来开始工作。

祝你好运,希望看到许多新的成功动量投资者!