接前面三篇关于程序化交易实盘代码的文章:
[硬核干货 - 量化交易系统的自动化实盘细节与思考(一 问题与难点)]
[量化交易系统 - 自动化实盘细节与思考(二 实盘宗旨)]
[量化交易系统 - 自动化实盘细节与思考(三 处理技巧)]
这里再继续说说多策略交易系统,实盘代码总体架构里的行情中心。
前言
交易这个行业,特别是期货合约,每隔一段时间就会有几个高杠杆交易大神闪现,十万本金轻松翻到上千万。但是,这些人几乎都如流星划过夜空,短暂的绚烂夺目之后,就沉寂消失不见。
而另外一群长期做交易的老帮菜,整日唯唯诺诺,嘴里念叨着什么未来不可知,敬畏市场,黑天鹅随时降临之类的丧气话,同时用着很低的杠杆来回开仓试错,方向犹如墙头草,左右摇摆,一点也没有高手的决绝与勇气。但不知为何,这一帮人却一直在市场上活跃着,杠杆虽低,但是仓位还不小。
在交易世界这个人性的修罗场,明星易得,寿星难寻。
作为一名活下来的量化交易员,我认为交易中比较重要的一点,就是意识到多策略的必要性。主观交易要实现多策略还是比较难的,因为人力有限(除非像机构那样雇佣多名交易员)7*24 小时运行的市场也容易错过入场点。但是程序化交易,还是比较容易实现的。程序化失去了人脑人眼对价格走势形态的识别能力,但是执行力更强,复制策略分散仓位的能力也更强,有利有弊。
多标的多策略多参数,资金仓位自然而然就分散开来。单次盈亏不再有刺激的大开大合,而是追求资金曲线尽量平滑。
总览
下图就是我的一组实盘代码的大致架构图。
image.png
整个架构分为两块。一块是独立的行情中心,另外一块就是负责策略逻辑实现的交易程序。
通常我是以一个账号启动一个程序,它们的策略参数和账号 key 以配置文件区别,这样就可以完全共享代码,代码版本也更容易管理。策略的一些参数,可以略有不同,主要是为了错开某些操作,不用突然同时运行。
之前策略少的时候,我以每一个子策略启动一个 Python 程序,到了后期发现服务器内存不够用了。因为一个 Python 程序,光自身就占用了大概 60M 内存,如果策略多一点,再加上复制到几个账号,那么很快就会占用大量内存。虽然可以加钱上更强大的服务器,但是这样管理维护也比较麻烦。最终就改成了现在这样,以账号分,同类策略在不同的币上(多币种多参数)为一组,然后一组策略为一个 Python app。运行下来感觉这个组合模式对于多账号多策略的布局很不错,代码管理,实盘运维都很方便。
一个子策略就一个 Python app 的模式好处是,代码可以简单很多。但是,试想,如果你的策略运行到排名前 40 名的大币上,然后每个币运行 3 个不同的策略,那么就是 120 个子策略,如果再来三个账号,就是 360 个子策略,这种模式就难以为继。
app 少的时候,可以先采用 tmux 这类终端让 log 信息实时输出,在程序运行初期监控就非常方便。后期代码稳定了,再采用 pm2 这类运维软件管理。以后定期分析 log 文件就行。
多标的多策略的情况下,独立的行情中心就得是标配了。获取的各种市场信息,可以在多个交易程序之间共享,也就是上图左边的那些策略模块。
行情中心
下图是一张更详细一点的行情中心模块图。
image.png首先,可以通过配置文件,设定需要获取的 k 线 symbols,这样以后方便更换品种。
如果是多周期的,那么可以获取它们的公约数周期 k 线,然后各个策略根据自己的需求再 resample 就可以了。
如果要简化一点,一劳永逸,那么直接获取 15 分钟的 k 线最好,因为中低频策略,特别是趋势跟随这类,周期低于 15 分钟的,长期来看基本很难赚钱。
例如,你有 15 分钟,1 小时,4 小时的策略,那么行情中心仅仅获取 15 分钟的 k 线就行了。如果 resample 之后大周期的 k 线长度不够,那么就得在启动时刻,把 15 分钟的 k 线在数据库中多保存一些。
因为 websocket 只是推送最近更新的 k 线信息,如果你的策略用到的 k 线周期比较大,而且回溯时间又长的话,例如 4 小时的 ma150,那么就是 600 个小时(25天)的数据,15 分钟一根 k 线的话,需要 2400 根。这个得靠在行情中心启动的时候,通过 rest api 一次性获取保存,作为之后不断更新的基础。
整个行情中心,我分了两个 app。其实也可以合在一个 app,但是分开更加 robust。先说次要的 PlanB。
PlanB
之前几篇文章说过,这个主要是防止 PlanA 的 websocket 掉链子的情况,用来临时备用的,特别是策略的出场时刻,避免价格都已经反向走出一段距离了,该退出仓位还没退出,造成意外的更多亏损。
PlanB 非常简单,主要就是利用 public_get_ticker_price 这个函数,一次性获取所有永续合约的最新价格。这也是B安自己聚合好的最新价格信息。
目前B安大概有 200 多个永续合约,如果一个一个单独获取最新价格的话,要保证每个品种价格 3 秒更新一次,一分钟内大概就要调用超过 4000 次 api,肯定超过限制了,不可能实现。但是前面提到的那个函数 api 权重只有 2,如果 3 秒一次,每分钟也才耗费 40 次 api quota,完全不用担心超标。
经过我的测验,基本上币安就是 1~2 秒左右聚合一次所有合约的价格,所以对于中低频策略的备用价格,5 秒左右的延迟完全够用了。
如果要求不高,你甚至可以用它来合成所有合约的粗糙 k 线,完全避开采用 websocket 的方案。这种 k 线就算不能用来交易,也可以用在诸如全市场信息扫描上,然后根据自己设计的策略算法,例如什么均线发散、收敛,简单的形态识别等等,给主观交易员提供入场信号。不然,那么多币,怎么看得过来。如果有水平高的日内交易员,这种工具可能就会有用,辅助交易。我之前就给别人写过类似工具,行情好的时候确实不错,不过最近没啥行情了。
当然,只有在交易很多币的时候这个方案才有实际用处。不然就直接获取各个品种的盘口 BBO 价格更好。
还有注意,备用价格信息也得在主代码中常常使用才行,比如用来计算仓位对应的法币资金额,大致盈亏率这类不需要很精确的数据。因为,不经常使用的代码片段和信息,早就崩坏了你可能还不知道,直到用时才发现就晚了。代码写多了的都知道,这种阑尾代码很容易意外改坏而不自知。
PlanA
行情中心的主代码。这个 app 主要有 4 个功能。
-
获取历史基础 k 线。就是文章前面提到的,每次启动,要保证所有策略需要的每根 k 线都在。
-
websocket 管理。这一个模块稍微复杂一点,需要处理各种掉线情况,还有重连等等。每次交易所推送了新的 k 线数据,就更新数据库中 k 线最新那一根 bar 的数据。
-
除了 k 线数据,我还把每个品种的精度信息更新也放在行情中心。也就是 public_get_exchangeinfo 这个函数获取的各种下单价格,下单数量的最小精度,再转换成方便使用的字典类型。这样所有策略可以共享。
-
最后就是检查,简单的 sanity check 就够了。获取的 k 线是否连续,没有遗漏,然后再定期与 rest api 获取的 k 线进行一一对比,与 PlanB 获取的价格是否差距过大等等。总之,就是多对比检查,有错误早发现。
这些大概就是我的中低频策略行情中心架构。如果需要,还可以把账号的更新信息通过 websocket 进行获取。但是这样做稍微麻烦一点,大多数时候用不到。
网友评论