开发前的准备
API开发者在开发过程中可以在量投quantfair环境中进行开发测试,QDP模拟环境的行情提供实盘行情,目前包括国内上期所、中金所、大商所、郑商所、广期所、能源交易中心和上金所。
测试帐号需要发送申请邮件到qdp@quantdo.com.cn,具体申请步骤见投资者测试申请。
API开发者在测试环境完成测试后转向实盘交易时,在期货公司或黄金综合类会员开户完成后,会取得诸如BrokerID、UserID、用户密码、交易前置地址和行情前置地址等交易相关信息,进行实盘交易。
开发流程
API接口初始化
以下步骤描述的是创建和初始化行情接口或交易接口的过程,通过以下步骤开启交易接口或行情接口的工作线程。在初始化阶段,程序必须完成如下步骤(具体代码请参考开发实例):
- 创建一个Api实例,这里的API实例是指接口中提供的CQdpFtdcTraderApi:: CreateFtdcTraderApi;
- 创建SPI实例,这里的SPI实例是指开发者创建的自己的类,该类已经继承了接口中的SPI 接口类(CQdpFtdcTraderSpi);
- 向Api实例注册(CQdpFtdcTraderApi::RegisterSpi)Spi实例;
- 订阅私有流SubscribePrivateTopic。 用于接收私有数据,如委托回报、成交回报。默认模式是从上次断开连接处继续收取交易所发布数据(Resume模式,对应的数据字典是QDP_TERT_RESUME)开发者还可以指定全部重新获取(对应的数据字典是QDP_TERT_RESTART),或从登陆后获取(对应的数据字典是QDP_TERT_QUICK)。如不订阅私有流,API将收不到客户的委托回报和成交回报等信息;
- 订阅公共流SubscribePublicTopic。 用于接收公有数据,如合约的交易状态。默认模式是从上次断开连接处继续收取QDP发布数据(对应的数据字典是QDP_TERT_RESUME),开发者还可以指定全部重新获取(对应的数据字典是QDP_TERT_RESTART),或从登陆后获取(对应的数据字典是QDP_TERT_QUICK)。如不订阅公共流,API将收不到合约的交易状态等信息;
- 向 API实例注册前置地址RegisterFront。 交易接口需要注册交易前置地址。相关地址在实盘交易时向期货公司或黄金综合类会员咨询;
- API实例调用Init方法,启动API线程。 关闭API线程需要调用Release方法,调用的线程不能在API线程内部;
- 等待API线程退出,调用Join接口。
在初始化阶段,程序只能调用订阅接口;在OnFrontConnected方法调用后必须调用ReqUserLogin向QDP服务端发起登录请求,请求成功后才可以任意调用交易接口中的请求方法,如ReqOrderInsert等,同时按照需要响应回调接口中的应答方法。在登录成功之前不能进行任何别的请求。
其他注意事项:
- API请求的输入参数不能为NULL。
- 20210329版本后,需要在登录成功后分别私有流和公有流请求ReqReady。
- 如果QDP服务端是需要认证的,则在登录前需要调用ReqAuthenticate向QDP发起登录认证请求,收到认证成功的Rsp后再进行登录请求。
连接和重连
API线程启动后会自动连接对应的交易前置,连接成功后会回调对应的SPI实例的OnFrontConnected方法,一般在这个回调方法中做请求登录操作,不做其它的操作;
如果一直没有回调这个方法,请检查API注册的前置地址是否正确,QDP柜台是否已经启动。
连接建立后QDP交易核心会分配一个SessionID给该连接,该SessionID在登录成功的应答里获取(用户本地报单号撤单时需要用到SessionID)。API线程会不断的通过这个连接和QDP交易核心发送心跳报文,这个心跳发送间隔默认为10秒,可以通过SetHeartbeatTimeout方法设置超时时间(必须在Init方法调用之前设置);
连接建立后必须尽快发起登录操作,也只能做登录操作,长时间不登录会被QDP交易核心断开连接。
连接断开时API线程会回调OnFrontDisconnected方法,该方法的参数nReason指明了连接断开的原因,一般都是网络连接断开或心跳超时导致的,心跳超时导致的端口需要客户自己检查API程序在回调时做了一些耗时的操作阻塞了API线程接收心跳报文,另外一个可以增加超时时间;
连接断开后API自己会定时重连QDP前置。重连成功后的SessionID与原来的SessionID是不同的。
认证
认证请求必须在连接已经建立的情况下发起,并且在登录之前。
QDP客户的穿透式监管,主要分为两种模式:直连(QDP的大部分客户)和中继
直连模式的流程如下:
- 期货 公司会在QDP后台根据客户提供的信息(经销商编码+交易终端+AppID/RelayAppID),生成对应的新的AppID和AuthCode,并且提供给客户;
- 客户通过ReqAuthenticate接口将AppID和AuthCode发给QDP;
- QDP的Spi会通过OnRspAuthenticate返回验证结果;
- 在验证成功之后,再进行登录,否则登录会报错(错误码为168,错误信息为“登录之前必须先进行APPID验证”)。
中继模式的流程如下:
使用中继模式接入qdp的系统,在直连模式流程的基础上,在每一个客户接入中继系统后,都需要调用api接口ReqSubmitUserSystemInfo上报客户端采集信息
- 需要利用qdpdatacollectapi中的QDGetLocalSystemInfo接口,采集对应的信息,参见信息采集API使用demo开发实例;
- 再调用ReqSubmitUserSystemInfo接口上报客户端采集信息。
注:如果是使用libqdpdatacollectapi.so,linux目前采用lshw命令采集硬盘序列号信息,请确保环境有该命令,否则登录返回错误码为6(采集硬盘序列号失败)。
认证请求
int ReqAuthenticate(CQdpFtdcAuthenticateField *pAuthenticate, int nRequestID);
用户登录认证请求信息CQdpFtdcAuthenticateField说明,详见ReqAuthenticate方法开发接口。
注意:认证请求信息需向期货公司获取。
认证应答
void OnRspAuthenticate(CQdpFtdcRtnAuthenticateField *pRtnAuthenticate, CQdpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
用户登录认证应答信息CQdpFtdcRtnAuthenticateField说明,详见OnRspAuthenticate方法开发接口。
登录
登录请求必须在连接已经建立的情况下发起(如果QDP服务端是需要认证的,则需要在认证成功后发起)。QDP支持同一个账号的多点登录。同时登录的账号能收到该账号的所有报单,包括该账号在其它连接下发起的报单请求。
登录请求
int ReqUserLogin (CQdpFtdcReqUserLoginField *pReqUserLogin, int nRequestID);
用户登录请求信息CQdpFtdcReqUserLoginField说明,详见ReqUserLogin方法开发接口。
登录应答
void OnRspUserLogin(CQdpFtdcRspUserLoginField *pRspUserLogin, CQdpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
用户登录应答信息CQdpFtdcRspUserLoginField说明,详见OnRspUserLogin方法开发接口。
获取投资者代码
API开发者可通过接口查询获取投资者代码,QDP报单时需要填写InvestorID,即投资者代码。
用户投资者查询请求
int ReqQryUserInvestor(CQdpFtdcQryUserInvestorField *pQryUserInvestor, int nRequestID)
用户投资者查询请求信息CQdpFtdcQryUserInvestorField说明,详见ReqQryUserInvestor方法开发接口。
用户投资者查询应答
void OnRspQryUserInvestor(CQdpFtdcRspUserInvestorField *pUserInvestor, CQdpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
用户投资者应答信息CQdpFtdcRspUserInvestorField说明,详见OnRspQryUserInvestor方法开发接口。
获取投资者资金信息
QDP的初始资金信息是从对应的主席系统导入的资金信息,比如期货公司都是结算后从CTP主席导入客户的资金信息,客户实际的资金额度应以主席柜台为准。
用户投资者资金信息查询请求
int ReqQryInvestorAccount(CQdpFtdcQryInvestorAccountField *pQryInvestorAccount, int nRequestID)
用户投资者资金查询请求信息CQdpFtdcQryInvestorAccountField说明,详见ReqQryInvestorAccount方法开发接口。
用户投资者资金信息查询应答
void OnRspQryInvestorAccount(CQdpFtdcRspInvestorAccountField *pRspInvestorAccount, CQdpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
用户投资者资金应答信息CQdpFtdcRspInvestorAccountField说明,详见OnRspQryInvestorAccount方法开发接口。
获取投资者持仓信息
QDP的初始持仓信息是从交易所查询得到的。如果盘中交易时客户在主席做了开平仓操作,那么次席QDP系统的持仓信息和主席系统的持仓信息会不匹配,因为在主席上做的报单成交流水都不会推送到次席QDP上来。
Position表示客户当前的持仓量;TodayPosition表示客户当前的今持仓量;Position-TodayPosition表示客户当前的昨仓数量;YdPosition不是客户的当前昨仓数量,它表示客户期初的昨持仓数量,它是个不变值。
目前上期所和能源交易中心提供平今和平昨指令,其它交易所只提供平仓指令。中金所和上金所平仓时优先平今,大商所和郑商所平仓时优先平昨。
用户投资者持仓查询请求
int ReqQryInvestorPosition (CQdpFtdcQryInvestorPositionField *pQryUserInvestorPosition, int nRequestID)
用户投资者持仓查询请求信息CQdpFtdcQryInvestorPositionField说明,详见ReqQryInvestorPosition方法开发接口。
用户投资者持仓查询应答
void OnRspQryUserInvestor(CQdpFtdcRspUserInvestorField *pUserInvestor, CQdpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
用户投资者应答信息CQdpFtdcRspUserInvestorField说明,详见OnRspQryInvestorPosition方法开发接口。
委托和成交
报单请求
int ReqOrderInsert(CQdpFtdcInputOrderField *pInputOrder, int nRequestID)
用户报单请求信息CQdpFtdcInputOrderField说明,详见ReqOrderInsert 方法开发接口。