前言
最近群里的小伙伴们都在讨论simnow
停用的事情,从3月31日开始,要持续一个半月,不出意外的话也要5月中旬才能恢复。于是很多搞CTA
的研发人员可能最近都面临着到哪里去找仿真环境的问题。
笔者不由得想起来,若干年前市面上还没有simnow
的时候,要找一个期货仿真环境真的是很麻烦。一方面要看期货公司是不是部署了仿真环境,只有期货公司有仿真环境才好测试;另一方面,能够交易的交易所和合约也是非常有限的;而且,所有的订单都需要对手盘,不然根本不会撮合,所以在测试的过程中还需要请期货公司的人帮忙下对手单,要不然就只有自己搞自成交了。
本文主要内容就是介绍如何利用WonderTrader搭建本地仿真环境。
TraderMocker简介
WonderTrader在v0.3.6
发布的时候,发布过一个TraderMocker
模块,基于该模块用户可以非常容易地搭建一个纯本地的仿真环境,而不用依赖任何第三方环境。
笔者最初在设计TraderMocker
的时候,正在给WonderTrader适配股票交易。当时接入的是中泰XTP
接口,XTP
比较流行也比较容易接入,有互联网的测试环境,API
也非常友好。
不过市面上很多股票测试环境多少都有一些问题,总结下来大概如下:
- 有些仿真环境是基于撮合的,如果没有对手盘,根本无法成交
- 有些仿真环境是按几率撮合的,如果下单时没有成交,那就永远不会成交了,不符合正常的撮合逻辑
- 有些仿真环境可能是没有接入实时行情,所以不管当前什么价格,价格如何变动,成交价都是挂单价
显然在这样的仿真环境下测试,策略仿真的结果其实是有很大的迷惑性的。在这样的情况下,笔者便决定自己开发一个仿真环境。但是WonderTrader作为一个量化交易框架,本身都是各用户独立部署的,也没有中心化的服务,开发一套C/S
的仿真环境,还需要硬件投入。对于WonderTrader这样的开源平台来说,成本太高的话是不现实的。另外仿真环境的部署一般要根据市场、品种分别部署,对于WonderTrader这样面向全市场全品种的平台来说,成本就更是翻了好几倍了。笔者纵然愿意分享源码,但是也没有办法持续性的投入资金去维护这样的仿真服务。基于这些基本考量,TraderMocker
这种全本地化的、去中心的仿真模块就应运而生了。
为了尽量的模拟真实的接口调用,TraderMocker
在设计上也有一些特点:
异步执行
异步执行的主要目的是为了还原真实交易的事件发生顺序。以下单为例,生产环境下,调用下单接口遵循以下数据: 下单接口调用-> 下单结果返回-> 订单回报-> 成交回报。如果不采用异步执行的机制,那么就会出现下单接口还没有返回的时候,已经收到订单回报和成交回报了,这样就不符合生产环境的真实场景了。
根据价格撮合
TraderMocker
自然不可能是一个完全的仿真环境,所以撮合的机制也相对简单。但是为了尽量模拟实盘环境,TraderMocker
会严格按照价格优先的机制进行撮合。这里的撮合,指的是不需要对手盘的撮合,即只要价格条件满足,即直接撮合成交,推送订单回报和成交回报。支持不同的品种
TraderMocker
作为一个辅助的简化的本地仿真模块,要充分考虑对不同的品种的支持。这样才能增加TraderMocker
的应用场景。
基于以上的设计原则,TraderMocker
也表现出一些特点:
仿真的程度有限
TraderMocker
毕竟不是真正的撮合系统,只是利用行情对订单做一个仿真处理。而且接入的行情,就算是期货,也是500ms
一笔的快照。另外,考虑到不同行情源档位不同,所以撮合处理的时候只利用了买一卖一的数据。TraderMocker
对订单之间的竞争也不考虑进去,统一按照最新的tick
的委托量进行处理。不适合大单测试
TraderMocker
并没有处理订单对限价订单簿的冲击。主要考虑到期货行情只有一档,无法进行冲击的处测算,所以就简化了。所以当一个订单的委托价格高于对手价的时候,一次撮合不完会等到下一笔tick
进来再利用对手价进行撮合,这显然不大符合真实场景。因此大单仿真的还原程度会更低,参考性也更低了。不做验资操作
TraderMocker
因为其特殊性,不会进行资金检查,只要合法的订单都能下单成功。一方面是因为TraderMocker
是为通用性设计的,所以无法兼顾所有的币种,如果增加验资的机制会增加复杂性。另外一方面, WonderTrader的 1+N执行架构,实际上是隔绝了策略对资金的关注,即使做验资,也无法将问题传递给策略。不做结算处理
TraderMocker
为了简化处理,不会进行结算处理。如果引入结算机制,会增加复杂性,而且还会要求仿真器一直在线直到接收到结算价为止。但是在策略的实操中,其实都主要关心的还是进场价和出厂价,结算的意义对于策略来说并不大。不过如果不结算,所有的持仓都是今仓,因此对于有些品种来说,仿真环境中的手续费的设置就需要把平今设置为跟平昨一致。订单都在内存中
对于
TraderMocker
这样的简易仿真模块, 只会把必要的数据落地。目前TraderMocker
只会保存持仓数据,而 订单数据和成交数据都只在内存中。一单平台重启,订单和成交就都都没有了。交易指令简化
TraderMocker
为了兼容不同的品种,所以 只能实现通用的指令,即 买卖和撤单的指令。其他的特殊指令就不支持了,比如ETF
申赎、期权的报价和执行等指令。
当然TraderMocker
只是笔者仓促写出来的一个简易的仿真模块,有很多仿真功能因为使用场景有限,并没有完全实现。如果各位读者有兴趣的话,可以自行根据自己的需求进行完善。到时候如果愿意分享给大家的话,也可以提交一个PR
。
如何搭建本地仿真环境
前文介绍了一下TraderMocker
模块,下面本文就将介绍如何在利用WonderTrader搭建这样的本地仿真环境。搭建本地仿真环境,需要用到datakit_fut
和hft_fut_mocker
两个demo
。这两个demo
笔者已经提交到wtpy/demos
下,有需要的读者可以自行获取,如果master
分支没有的话,请到dev
分支下载即可。
行情配置
datakit_fut
是通过CTP
接口落地行情数据的数据组件demo
,基本配置如下:{ "basefiles":{ "session":"./common/sessions.json", "commodity":"./common/commodities.json", "contract":"./common/contracts.json", "holiday":"./common/holidays.json" }, "writer":{ "path":"./FUT_Data/", "savelog":false, "async":false, "groupsize":20 }, "parsers":[ { "active":true, "module":"ParserCTP.dll", "front":"tcp://180.168.146.187:10111", "broker":"9999", "user":"你的SIMNOW账号", "pass":"你的SIMNOW密码", "code":"CFFEX.IF2005,SHFE.au2012" } ], "broadcaster":{ "active":true, "bport":3997, "broadcast":[ { "host":"255.255.255.255", "port":9001, "type":2 } ] }
在使用的时候,将parsers
小节的CTP
前置和账号密码改成生产环境,并将code
改成自己需要的合约代码进行订阅,然后启动runDT.py
就可以正常运行了。
仿真配置
hft_fut_mocker
则是从UDP
广播通道接入行情,并调用TraderMocker
进行仿真,配置如下:{ "basefiles":{ "session":"./common/sessions.json", "commodity":"./common/commodities.json", "contract":"./common/contracts.json", "holiday":"./common/holidays.json", "hot":"./common/hots.json" }, "env":{ "name":"hft", "mode": "product", "product":{ "session":"TRADING" }, "filters":"filters.json", "fees":"fees.json", }, "data":{ "store":{ "path":"./FUT_Data/" } }, "traders":[ { "active":true, "id":"mocker", "module":"TraderMocker.dll", "front":"mocker://localhost", "mockerid":9999, "span":100, "newpx":true, "maxqty":100, "minqty":1, "user":"mocker9999", "udp_port":9001, "savedata":true } ], "parsers":[ { "active":true, "id":"parser1", "module":"ParserUDP.dll", "host":"127.0.0.1", "bport":9001, "sport":3997, "filter":"" } ], "bspolicy":"actpolicy.json" }
从上面的配置可以看出,TraderMocker
仿真器,要从udp
广播通道接收最新的行情,才能进行撮合。可能有人会有疑问:为什么不从行情通道通过WonderTrader直接向TraderMocker
传递行情数据呢?其实也很好解释,因为Trader
模块解耦以后,WonderTrader只和Trader
交互交易数据,而行情数据,不在接口支持的数据范围内,所以TraderMocker
只能自己解决行情接入的问题。因此TraderMocker
自然就会依赖数据伺服组件提供的行情广播服务了。
配置修改好了以后,再检查一下策略启动入口:
from wtpy import WtEngine,EngineType
from strategies.HftStraDemo import HftStraDemo
if __name__ == "__main__":
#创建一个运行环境,并加入策略
engine = WtEngine(EngineType.ET_HFT)
engine.init('./common/', "config.json")
engine.commitConfig()
straInfo = HftStraDemo(name="hft_IF", code="CFFEX.IF.2104", expsecs=5, offset=100, freq=0)
engine.add_hft_strategy(straInfo, 'mocker')
engine.run()
kw = input('press any key to exit\n')
最后,运行run.py
就可以正常进行仿真测试了,如图:
如果有需要进行股票仿真的,只需要修改./common
目录下相应的基础文件,并修改配置文件中的行情接入模块的配置文件即可。如有不明白的地方,读者也可以私信咨询笔者。
结束语
如何利用WonderTrader的仿真模块TraderMocker
来搭建本地的仿真环境,总结下来就是两个步骤:先运行一个数据伺服,再运行仿真环境。简单的两个步骤,就可以搞定一个仿真环境。相信对于有需要的人来说,这是一个很轻松的事情。
对于想用simnow
的用户来说,在simnow
缺位的这段时间,只需要一个CTP
s实盘账号即可进行仿真测试。对于那些不满足于券商提供的股票仿真环境的人来说,本文介绍的这个攻略也许可以提升你仿真测试的效率。
当然,笔者水平有限,TraderMocker
的开发也比较仓促,难免有很多错漏之处,各位读者在使用的时候还需要自行判断一下是否满足自己的需求。另外,仿真毕竟不是实盘,策略的表现是否合理还需要各位仔细辨别。
最后再安利一下WonderTrader
WonderTrader旨在给各位量化从业人员提供更好的轮子,将技术相关的东西都封装在平台中,力求给策略研发带来更好的策略开发体验。
WonderTrader的github
地址:https://github.com/wondertrad...
WonderTrader官网地址:https://wondertrader.github.io
wtpy的github
地址:https://github.com/wondertrad...
市场有风险,投资需谨慎。以上陈述仅作为对于历史事件的回顾,不代表对未来的观点,同时不作为任何投资建议。