接下来是比较核心的部分
CtpTdApi(TdApi)
缓存变量
- 状态变量:包括控制连接、登录、授权、连接失败,四个控制变量,比如connect_status
- 用户信息:比如userid
- frontid;sessionid[用于形成唯一order序号,由Thost回调提供]
- reqid;order_ref[vnpy内部维护,用于发单等编号]
- order_data;trade_data[这两个list用于保存第一次私有流委托等回报的data]
- sysid_orderid_map用于
- symbol_\exchange_map;symbol_name_map;symbol_size_map[这三个是全局dict,用于在合约代码、交易所、合约名称、合约大小之间形成映射]
逻辑流程
先明确作为一个交易接口,TdApi需要实现什么功能:
- 首先tdapi需要能够正常连接到ctp服务器,即执行连接、登录操作
- 其次tdapi需要在第一次连接后查询各个交易所可交易合约并保存到vnpy本地;查询账户资金情况;查询持仓情况;查询当前报单情况;查询当前成交情况;
- 最后就是通过交易接口进行一些基础的下单操作,并且得收到服务器多次响应。
其次要明确CTP交易接口如何实现这些功能
由于客户端和期货公司CTP服务器在通讯过程中设计多种模式,先初步介绍一下CTP通讯模式
CTP-API涉及的通讯模式有三种:对话通讯模式;私有通讯模式;广播通讯模式。而不同的通讯模式又支持不同的数据流,具体体现在函数命名规则上 - 对话通讯模式:客户端主动发起请求,服务器端给予相应回应,该模式支持对话数据流和查询数据流。前者指的是客户端向服务器发送交易请求,包括登录、报撤单等,Api命名规则为Reqxxx;后者指的是客户端向服务器发送查询请求,比如报单查询、合约查询等,命名规则为ReqQryxxx。
- 私有通讯模式:CTP后台主动向某个特定客户端发出信息,该模式支持私有数据流,并且仅有响应接口OnRtnxxx,没有请求接口,包含成交回报、报单回报、各种错误回报。成交回报指的是报单状态信息变化后CTP主动向客户端发送回报信息。
- 广播通讯模式:CTP后台主动向所有客户端发出信息,比如合约交易状态通知
私有流和交易接口又相关关联,比如通过对话流进行报单录入,不仅会收到报单相应OnRspOrderInsert,当报单交易状态发生变化时,又会自动调用OnRtnOrder
CTP实现一个交易接口的功能可以分成几部分:初始化、初次查询、报单录入、撤单、委托成交回报。
- 初始化
connect(初始化连接)--> onFrontConnected(连接响应,嵌套authenticate) --> onRspAuthenticate(授权成功,嵌套login) --> login(实际触发reqUserLogin) --> onRspUserLogin(登录回报,嵌套投资者结算结果确认reqSettlementInfoConfirm,注交易接口和行情接口区别之一在于交易接口在客户端成功登录ctp后会主动进行私有流\公有流回报OnRtnxxx,并且比投资者结算结果响应还要早)
完成上述操作后交易接口初始化完成,但仍然不适合直接进行交易,还需要进行初次查询,而vnpy直接将这两种操作互相嵌套了。
注:初始化最后会自动调用onRtnOrder和onRtnTrade作为私有流回报。原生OnRtnOrder只用于返回报单信息,通知客户端报单的最新状态.vnpy重载形成onRtnOrder,想要实现的功能包括第一次连入Thost后将挂单缓存到本地,vnpy发出的报单得到的委托成交回报也缓存到本地。
具体逻辑为 :和order相关的vnpy内部数据结构有OrderRequest,OrderData.OrderData向vnpy上层事件引擎推送信息,来源于Thost回调的data数据,通过预定义的dict映射成OrderData.OrderRequest是策略层下底层发单用到的对象,和OrderData区别在于不包含接口信息。由于Thost的委托回报自身没有交易所信息(空字段),并且vnpy三个映射字典是通过reqQryInstrument完成的(比第一次私有流回报晚),因此OnRtnOrder函数需要同时考虑几种情况- 第一次连入Thost:将onRtnOrder返回的CThostOrderField数据缓存在self.order\_data中 - 在onRspQryInstrument内部:在最后一次查询合约完成后,vnpy内部三个映射dict已经全部缓存完成,这时将order\_data中的各个CThostOrderField手工推送到onRtnOrder中 **onRtnOrder完成的功能**:将Thost推送的委托回报数据包装成OrderData推送到gateway中;将委托回报的orderid和OrderSysID记录成映射
- 初次查询
除了嵌套在初始化中的查询合约,vnpy还对账户信息和仓位进行轮询。查询结果全部封成vnpy内部数据结构推送给gateway。
- 报单
在vnpy系统里,上层传到底层的报单对象为OrderRequest,然后在gateway层将其转化为ctp所需要的dict对象(pybind会把dict变成C++里的struct)。
报单指令是ReqOrderInsert,CTP所需要的报单对象有如下成员:
以上是报单基础类型,可以根据OrderRequest.type进行diy,比如变成FAK或者FOK,只需要更改TimeCondition和VolumeCondition- InstrumentID,ExchangeID,LimitPrice,VolumeTotalOriginal(数量) - OrderPriceType(订单价格类型,比如限价等,vnpy支持限价、市价、stoop、FAKFOK) - Direction(买卖方向) - CombOffsetFlag(组合开平标志,一般用不到) - OrderRef(报单引用,作为报单的标识之一,vnpy内部会维护) - InvestorID(投资者代码),BrokerID(经纪商代码) - ComHedgeFlag(组合投机套保标志,一般用不到) - ContingentCondition(触发条件,比如立即、止损等,vnpy默认使用“立即”) - IsAutoSuspend(自动挂起标志,bool类型,默认是False,不知道干啥的,不重要) - TimeCondition(有效期类型,包含FOK、当日有效、本节有效、指定日期前有效、撤销前有效、集合竞价有效,vnpy发单默认使用“当日有效”) - VolumeCondition(成交量类型,vnpy默认使用任何数量) - MinVolume(vnpy默认最小成交数量是1)
报单涉及到一组唯一序号,由FrontID、SessionID、OrderRef组成,前两个是在登录后ctp默认分发的,vnpy会自动缓存,第三个vnpy在每次报单操作后会自动++1递增。这三个成员组成了某一个接口中独一无二的order编号orderid。而在往上层推送的是利用OrderRequest.create_order_data生成的OrderData对象,多出了vt_symbol,vt_orderid等信息。- FAK = 立即完成所有数量否则撤销 - FOK = 立即完成任何数量否则撤销
至此,一次报单请求操作已经完成。