我在币安做空超涨做多超跌多币种对冲策略时,同时发布了一个回测引擎。并第一篇报告基于一小时K线回测,验证了策略的有效性。但实际公开策略的休眠时间时1s,是一个相当高频的策略,用小时K线回测显然无法得出精确结果。后来补充了分钟线回测的结果,回测收益提高了很多,但还是无法确定秒级情况下应该用什么参数,对整个策略的理解也不是很清晰。主要原因是基于K线的回测的重要弊端。
基于K线回测的问题
首先什么是历史K线?一根K线数据包含高开低收四个价格、起始两个时间以及区间成交量。大部分量化平台和框架都是基于K线回测的,FMZ量化平台也提供了tick级回测。K线回测的速度很快,大部分情况下也没问题,但是也有非常严重的缺陷,特别是回测多品种策略和高频策略,几乎无法得出正确的结论。
首先是时间问题,K线数据最高价和最低价的时间是没有给出的,不用考虑,但最重要的开盘和收盘价起始并不是开盘和收盘时间。即使不太冷门的交易品种,也往往十几秒都没有交易,而我们回测多品种策略时,往往默认它们的开盘价和收盘价是同时的,这也是基于收盘价回测的基础。
想象一下用分钟线回测两个品种的套利,它们的差价通常10元,现在发现10:01时刻,A合约收盘价为100,B合约为112,差价是12元,于是策略开始对冲,某个时刻差价回归,策略赚了2元的回归利润。
而实际情况可能是在10:00:45,A合约产生了一笔100元的成交,此后没有交易,B合约在10:00:58发生了一笔112元的成交,在10:01这一刻,两个价格都是不存在的,此时的盘口价格是多少呢,对冲能够吃到多少差价呢?都无法知道。一个可能的情况是:在10:00:58时,A合约的买一卖一盘口是101.9-102.1,根本没有2元差价。这就会对我们的策略优化产生很大的误导。
其次是撮合问题,真实的撮合是价格优先,时间优先。如果买家超过卖一价,一般会直接以卖一价成交,反之进入订单簿等待。K线数据显然没有买一卖一价,是无法模拟细节层次的撮合。
最后是策略本身成交对市场的影响,如果是小资金回测,影响不大。但如果成交量占比很大,会对市场产生冲击。不仅是立即成交时价格滑点会很大,如果回测你的买单成交了,实际上抢占了其它原来要买入交易者的成交,蝴蝶效应下会对市场产生影响。而这种影响无法量化给出,只能凭借经验说高频交易只能容纳小资金。
基于实时深度和tick的回测
FMZ提供了实盘级回测,能够获取到真实的历史20档深度,实时的秒级tick,逐笔成交等数据,并基于此做了实盘回放功能。这样的回测数据量极大,回测速度也很慢,一般只能回测两天。对于相对高频或者对时间判断要求严格的策略,实盘级回测是必须的。FMZ收集的交易对和时间并不长,但也有700多亿条历史数据。目前的撮合机制是如果买单大于卖一会不看量立即完全撮合,小于卖一进入撮合队列排队。这样的回测机制解决了K线回测的前两个问题,但还是无法解决最后一个问题。并且由于数据量实在太大,回测速度和时间范围都有限制。
基于逐笔成交订单流的回测机制
K线信息太少,深度也有可能是假深度,但有一种数据是市场真实的成交意愿,反映了最真实的交易历史———那就是逐笔成交。本文将提出一个基于订单流的高频回测系统,将大大减少实盘级回测的数据量,并且一定程度的模拟成交量对市场的影响。
我下载了最近5天币安XTZ永续合约的逐笔成交,作为一个不算热门的品种,共有213000条数据,先看一下数据的构成:
数据是二维列表,按成交时间顺序排序。具体意义分别是:品种名称、成交价格、成交时间戳、成交数量、是否是卖单主动成交。有买有卖,每一笔成交都包含了买方和卖方,如果买方是做市maker,卖方是主动成交taker,则最后一个数据是True。
首先根据成交方向,可以相当精确的推测出市场上的买一和卖一,如果是主动卖出单,则此时的买一价就是成交价,如果主动买入单,则卖一价为成交价,有新的成交就更新新的盘口,未更新的保留上一次结果。很容易的推出以上数据的最后时刻,买一价为2.903,卖一价为2.904。
根据订单流,可以这样撮合:以一笔买单为例,价格为price,下单量为amount,此时盘口买一卖一分别为bid,ask。如果price低于ask高于bid,则先判断为maker,并且可以优先撮合成交,则此后在订单存在时间内所有的成交价低于或等于price的逐笔成交都与此订单撮合(如果price低于或等于bid,则不能优先成交,成交价低于price的订单都与此订单撮合),撮合价为price,交易量为逐笔成交的成交量,直到订单完全成交或者撤单。如果价格高于ask,判断为taker,此后在订单存在时间内所有的成交价低于或等于price的逐笔成交都与此订单撮合,撮合价为逐笔成交的成交价。区分maker和taker是因为基本上交易所鼓励挂单,有手续费的优惠,对于高频策略,必须考虑这种区别。
可以很容易看到这种撮合的一个问题,如果订单为taker,实际情况是能立即成交,而不是等待新的订单与之撮合。首先我们并没有考虑盘口挂单量,就算有数据,直接判断成交也改变了深度,影响了市场。而基于新订单的撮合,相当于把历史中真实存在的订单替换成你的订单,无论如何也不会超出市场本身成交量的限制,最终盈利也不可能超过行情产生的最大盈利。部分的撮合机制也影响了订单的成交量,进而影响策略的收益,定量的反映出了策略容量。不会出现传统回测,资金量放大一倍收益就放大一倍的情况。
还有一些小细节,如果订单买价等于买一,实际上仍然有一定的概率以买一价被撮合的,需要考虑挂单的优先级和成交概率等,较为复杂,这里就不考虑了。
撮合代码
交易所对象可以参考开头的介绍,基本不变,只添加了maker和taker手续费的区别,以及优化了回测的速度。下面将主要介绍撮合代码。
几个细节要注意一下:
1.当有新成交时,要先去撮合订单,再去根据最新的价格去下单。
2.每个订单都有两个属性:maker——是否为maker,priority——撮合优先级,以买单为例,当买价小于卖一,标记为maker,当买价大于买一是标记为优先撮合,priority决定了价格等于买价是是否撮合,maker决定了手续费。
3.订单的maker和priority是更新的,如下了一笔很大的超过盘口的买单,当出现一个价格大于买价时,此时剩余的成交量将是maker。
4.策略的intervel是必须的,它可以代表行情的延时。
网格策略的回测
终于到了实际的回测阶段,我们这里回测一个最经典的网格策略,来看看有没有达到预期的效果。策略原理为价格每上涨1%,我们就持有一定价值的空单(反之持有多单),计算好买单卖单提前挂好。代码就不放出了。把所有代码封装到Grid('XTZ',100,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)函数中,参数分别为:交易对,价格偏离1%的持有价值,挂单密度0.3%,休眠间隔ms,挂单手续费,吃单手续费。
最近5天XTZ的行情处于震荡阶段,很适合网格。?
我们先回测不同的持仓大小对收益的影响,传统的回测机制回测出来的收益肯定会随着持仓的增加等比增加。
共回测了四组,持仓价值分别为100,1000,10000,100000,回测总用时1.3s。结果如下:
可以看到最终已实现利润分别为持仓价值的28.4%,27.5%,26.9%,22.6%。这也符合实际情况,持仓的价值越大,挂单的价值越大,越可能出现部分成交的情况,最终实现的收益相对于挂单量也就越小。下图是持仓价值分别为100和10000的相对收益对比:
我们还可以回测不同的参数对回测收益的影响,如挂单密度、休眠时间、手续费等。以休眠时间为例,改为100ms,对比休眠时间1000ms,看看收益情况。回测结果如下:
收益提高了一些,这是由于策略只挂了一组订单,会有一些订单由于来不及改变而吃不到波动的价格,休眠时间减少改善了这个问题。这也说明了网格策略挂多组订单的重要性。
总结
本文创新的提出了一种新的基于订单流的回测系统,可以部分模拟挂单、吃单、部分成交、延时等撮合情况,部分反映出了策略资金量对收益的影响,对于高频策略和对冲策略有重要的参考价值,高精度的回测为策略参数优化指明了方向。也经过了长期实盘验证。并且较好的控制了回测所需的数据量,回测速度也非常快。
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。