为股票和加密货币交易构建规则引擎 - ksred

22-04-12 banq

我建立了一个平台和市场来创建、测试和运行股票和加密货币的交易策略。

至少在过去的十年里,我对金融和API有着浓厚的兴趣,这促使我建立了像BVNK这样的初创公司和像COVID19API这样的产品。我对投资也有积极的兴趣,作为一名软件工程师,我对技术如何为某项任务带来更多的价值很感兴趣。

在阅读了一些关于交易策略的书籍后,我决定尝试实施这些策略,看看它们是否有效。当时有几个选项可以做到这一点,比如Quantopian(RIP),Trading View和Meta Trader平台。然而,这些软件要么学习曲线很陡,涉及我不熟悉的语言(Python和MQL),要么过于简单,只提供一些子集的功能。

对于一个特定的交易策略,我对以下内容感兴趣。
  • 股票的价格应该低于某个值
  • 股票的市值应低于某一数值
  • 在一定时期内,该股票应该有一定数量的内幕交易报告
  • 在一定时期内,股票的交易量应该有一定比例的增长
  • 在一定时期内,SMA20应跨越SMA50
  • 市盈率应低于某一数值

我发现没有一个平台能够轻松做到这一点,既能进行回溯测试,又能进行日常运行,因此我决定建立一个平台来实现这一点。

注意:虽然我在这篇文章中只提到了股票,但一切都与货币有关,当然,有些标准并不适用。

启动项目
我以前曾试图建立这个项目,并使其正常工作,但后来把自己编进了一个洞。它变得太复杂了,无法修改,如果不进行重大的重构,就无法测试。我还没有准备好放弃这个想法,所以决定重新开始,这次要好好做。

从第一天开始,我就使用了测试驱动开发,以确保代码能够被维护下去。在这个应用程序的构建过程中,这绝对是一个救命稻草,而且令人惊讶的是,它并没有减慢开发速度。我发现,当我脱离了TDD的习惯("让我快速完成这个任务"),花在调试和修复上的时间比一开始做TDD的时间还多。

不过TDD并不能自动带来好的架构,所以在开始开发后不久,我就花时间设计平台的整体架构。当我通过预期的结果工作时,我意识到这比我预期的要复杂得多。

隐藏的复杂性
我们的愿望是:能够创建一个具有N个进入标准和N个退出标准的方法论。这些标准在内部应该是同一类型的,所以我利用Go的接口来实现一个良好的模式。

注意:接口对于在Go中使用TDD并确保获得良好的覆盖率至关重要,所以我建议使用大量的接口和大量的嘲弄。

现在有几种类型需要实现:标准类型("你在测试什么"),标准状态类型("你在测试什么"),以及方向("高于或低于")。为了达到这一点,我花了很多时间,但在实现了这个基础后,我有了一个东西,a)可以工作,b)可以随着我在整个系统中增加更多的类型而扩展。

标准类型的例子有:目标、名义增长、百分比增长和交叉。
标准状态类型的例子有:成交量、价格、pe比率、指标、内部交易、目标等。

我能够得到的最好的工作方式是,你创建一个具有相关字段和数据点的标准对象,然后在评估过程中,你使用switch语句将标准指向一个可以最终完成评估的特定函数。

如果所有的标准点都被满足,则标准被标记为满足。如果所有标准都满足,那么相关的交易就会被执行(开仓或平仓)。

处理指标一直是特别困难的,主要是由于字段的数量不同和数据类型不同。一种方法是将每个指标作为一个对象在代码中创建,这比较容易,但会导致大量的代码。另一种方法是创建一个更通用的指标对象,它可以有一个相关的类型,并且字段和功能根据类型的不同而不同。我选择了后者,因为前期的设计将在未来节省很多时间(例如,如果你想给所有的指标添加一个字段,或者改变所有指标的行为)。

去耦合
通过TDD开发,你最终会得到漂亮的、解耦的代码。这使得改变变得更加容易,因为你可以确定这些改变不会产生意想不到的效果(大部分时间)。然而,在处理顶级领域类型的流程时,它确实会引起一些头痛。

在我的案例中,领域流程是一个“方法论”。( banq:DDD聚合根?)
“方法论”持有所有的标准和相关的交易,以及与特定用户的链接,还有一些元数据。
对象中不包括一些关键元素:股票本身,围绕股票的价格数据,以及跨方法论运行的交易。

我们的想法是,股票应该被传递给“方法论”,并且只是运行,而方法论不应该关心或知道股票的情况。但是,因为方法论不知道股票,所以价格也没有联系。为了清楚起见,流程如下。

  • 方法论被运行,所有标准被检查
  • 方法论返回标准状态,然后开启交易。

因为标准是灵活的,而且因为我们检查标准时,没有可用的价格信息。
你可以对方法论进行重载,添加一个定价字段和一个股票字段,但这与方法论对象没有联系--这是影响它的东西。我像躲避那场瘟疫一样躲避污染,因为它总是以泪水收场。

相反,为了解决这个问题,当交易需要打开时,我们获取股票的相关价格。我在这里说 "相关 "是因为我们也在进行回测,所以我们不能只取最新的价格,我们需要取一个有一定偏移量的价格。

数据管理
我正在使用IEX云作为数据源,正如任何与金融数据打交道的人所知道的,成本会迅速增加。我只想在我需要的时候获取数据,而且只在我需要的时候获取。

这发生在几个地方:当运行回测和请求数据时。因此,当这些事件发生时,我做以下检查。

  • 获取最新的数据记录(价格、公司统计、内幕交易,等等)
  • 如果有一个记录,检查日期。如果没有记录,就把日期设置为UTC 0
  • 将日期发送到一个函数,以返回要获取多少数据。如果我们落后5天,就获取1个月的数据(最小的增量),如果我们落后40天,获取3个月的数据,等等。另外,确保检查你是否在周末附近,并作出相应的调整。
  • 取出数据并保存,然后返回数据

由于后面的测试都使用了这些相同的函数,因此必须根据我们想要的数据来切割所得到的片断。如果我们想要60个时期以前的数据,我们就需要只返回到那个时候的数据。

上述数据的获取在直接通过API请求数据时也是有效的,而且令人惊讶的是根本不需要花费太长时间。例如,当请求3个月的数据时,我们从IEX获取数据,保存它,并在500ms左右返回。随后的调用要快得多,保存数据时<200ms,缓存数据时<100ms。

目前的状况
我在这个项目上花了六个月的时间(大概350个小时),写了23000行代码,最终的代码覆盖率将达到90%。这是一种爱的劳动,也是一种巨大的回报。

在向公众发布之前,该应用程序需要进行一些小的调整和进一步的测试,但它目前正在运行我的方法论中的大部分标准,每天大约有3000只股票。我将在短期内进行前端工作,并应在1月底前推出一些初始版本。我还向YCombinator申请了投资,让我们看看会发生什么。
 

1