前一篇硬核干货 - 量化交易系统的自动化实盘细节与思考(一 问题与难点)讲了一点币圈实盘的基本难题。这一篇说说实盘的主要宗旨。
我认为主要有四个:
一. 贯彻策略的既定逻辑
这一点毋庸置疑。在有信号的时候必须做出相应的操作,开仓,平仓。这一点就要求尽量不要用限价单,不然成交不了还得追单,可能价格都已经飞远了,最后到底要不要追也是个问题。
长期来看,采用市价单产生的滑点和偶尔错过限价单,但又追单产生的滑点总量大致类似,所以还不如直接市价进出场,除非你的策略很特殊。
另外,使用限价单的话,你的代码更复杂,因为要管理没有成交的限价单,再去追单。还有如果限价单只成交了一部分又怎么办?是余下的限价单一直挂着呢?还是错过部分再另外发市价单?这种一步操作却又产生多笔成交订单,后期复盘也麻烦,容易搞混。所以,不如直接用市价单算了,只要提交成功,基本上就会保证成交。如果确实是滑点敏感的策略,那么只做流动性好的标的吧。
还有,合约的话,杠杆也不要随意变动,最好是回测的理想杠杆再打八折。不要人为主观去预测行情,手动调整杠杆。仓位变来变去也是自动化交易的大忌。
仓位最好是自动赢冲输缩,回撤到一定程度就要降低杠杆,而不要反其道而行之,以为马上风就要来了,加大杠杆(不过可以加本金、降杠杆)。账户活命最重要,先求不败。可以等资金曲线度过回撤期,再加回杠杆。不过如果是采用复利模式,就已经是简单的自动赢冲输缩了。
二. 尽量减少滑点
这点前一篇已经说了。滑点是消除不了的,只能缓解。这需要你能在第一时间获取市场价格信息,交易所账号里的各种资金、仓位信息,才能做到快速反应。
实盘总体架构上,子策略多,又要求获取各类信息及时,就要有一个专门的 websocket 行情中心来分担任务。再配一个 Restful 的 Plan B 行情中心做备份。这样万一 websocket 掉线连接不上的时候,还有一个比较新的价格来判断是否需要离场。跑路的时候要快,宁愿跑错,也比被堵在里面,不知未来如何要强。
三. 长久运行
这一点前一篇也已经说过。币圈是 7*24 运行,你的代码是不能停机的。这就要求代码不会被抛出的各种异常中断,得有处理各种意外情况的代码。还有不能带有内存泄露这种 bug,不然时间长了,程序会崩溃,甚至整个服务器都被拖垮。好在 Python 不容易导致这类问题,它有内存自动回收机制。实在不行,每天或者几天手动重启一下交易程序也可以,反正都是中低频交易。这些都是基本的后端代码开发要求,容易达成。
一般量化交易都是采用 Python。Python 这种脚本语言一大特点就是动态编译,运行效率稍低,因为一边编译一边执行(不过完全不会影响到中低频交易)不容易调试。如果代码逻辑上暗含问题,可能不太容易被发现。 譬如有 100 行代码,第99 行有问题,但是每次都只运行到 95 行,那这个问题就不会被发现,因为必须运行到第 99 行,触发了相关代码,才能发现错误,但是这时候可能为时已晚,程序会直接崩溃,如果你没有 try catch exception 的话。
另外一个特点,其实已经包含在上面提到的问题里了。因为 Python 算是强类型,很少隐式转换类型。如果你太信任服务器传来的数据类型,特别是那些数字,那么很可能会吃亏,因为有的时候数字会变成字符串。交易所不同的币种,不同 API 版本,websocket 和 rest 传来的同一种返回数据,都有可能是不同类型。所以原则上,每次都必须强制转型一下,或者先判断需不需要转。
还有就是,各个策略之间开关仓不要搞混了,特别是一个币种的,做好隔离。不然在一些极端行情下,a 策略把 b 策略的仓位平了,最后导致对不上,可能得停掉实盘再重新开局,这样就不连贯了,也可能错过关键行情。
一句话,一切为了实盘的长期全自动化无干预运行。
然而,如果策略太过复杂,要做到完全无需干预,也不是那么容易的。简化策略也是一个入手点,别搞太复杂了。例如少用 OBV 这类信号,因为 OBV 需要追溯很久远的价量信息,实盘需要维护太多数据了,每次中断,重启都是一个麻烦事。不得已,避免这样的因子。
四. 风控
这点是实盘要求的重中之重。
其中最危险的,莫过于不能平仓。行情反向了,而且还在继续暴走,结果你的出场代码失控,不管什么原因,不止损平仓了。在币圈,山寨币短时间内是可能涨几倍的。所以,在做空的情况下,就算你没有使用杠杆,还半仓,自以为高枕无忧,结果早上起来仓位可能已经爆干净了。
所以,要保证退出机制一定生效。开仓机会如果错过至少不会致命,踏空了顶多惋惜,但是错过平仓是要出大问题的。
这里简单说两个解决方案,后面有空再详细讲讲。
第一点,就是最好要有一个硬止损,就是类似固定亏损比例的止损。例如,大币亏损超过 10%,小币 15%,发生了立马平仓跑路。
硬止损这种方法在开仓时刻就应该定下止损价位了。所以开仓后应尽快提交算法止损单(有的也叫条件单),中途无须更改价格。这样交易所帮你负责监控实时价格,触发之后再帮你提交市价单退场。这样处理可能会有不少滑点,但是大概率比你自己搞的止损稳定可靠,而且就算出问题了,没有触发,你还可能找交易所维权索赔(所以得去头部大交易所)。
当然,这种止损就是用来兜底的,应该极少触发才对,最好还是由自己的策略来控制出场时机,这样回撤大概更小。
第二点,程序不会乱开仓。不要已经开了仓,但是却以为没有,连续开仓,结果开出一个很大敞口。短时间内价格没有波动还好,还有可能手动介入纠正,要是突然反向,就可能爆仓的,这取决于你到底开出了多少仓位。当然这种是低级错误,但是确实可能发生。特别是中途迭代升级了代码之后,有可能考虑不周。
这种就需要先放慢一点开仓的节奏,必须等待开仓结果的返回。还有开仓前一定要查询确认当时的账户信息,不要连续开仓,当然也可以在本地记录所有操作,那么就省却一点查询交易所自己账号情况的 API 请求次数和时间。
还有,更有效的是,可以事先限制交易所的最大杠杆。这个是可以自己调整的。币安默认 20 倍,你改为 3 倍,甚至 2 倍(当然,这要求你策略本身就不使用高杠杠,不然你该开高点的时候还开不出来)。那么代码再怎么发神经,也不可能在你睡觉的时候搞出来一个大敞口。这样就比较稳妥。
总之,实盘有时候不光是交易代码,还需要布局一道道其他防线,防止被黑天鹅事件一下击穿。任何时候都不能掉以轻心,尽量封住死角。