“众所周知,天下大坑共一石,CTP独占八斗。进行CTP开发时,里面需要注意的地方比较多。今天给大家聊一聊CTP中暗藏的一些坑,让新手避开陷阱。”
CTP开发中有很多需要注意的小细节,稍有不慎就会出问题,不然,轻则表现与预期不符,重则程序崩溃影响策略盈利。本系列将容易遇到的坑列出来,以供开发时参考,如有疑义之处,欢迎指正。
01
【资料图】
成交通知OnRtnTrade中的开平标志是怎样的?
成交通知里的开平方向标志OffsetFlag,与其对应的报单里的开平方向CombOffsetFlag[0],其实并不是完全相等的。
对于上期所和能源中心,成交与报单的开平方向,是相同的。而对大商所、郑商所和中金所,如果是开仓,则成交通知中的开平标志也是开仓THOST_FTDC_OF_Open ('0');如果是平仓,则无论你报单时使用的开平方向是什么(平今,平昨等),成交通知中的开平方向都是THOST_FTDC_OF_Close ('1')。
值得一提的是,SimNow环境(怎么又是你?)中,成交中的开平标志的设置是遵循上期所的规则的,即成交随着报单的开平标志来,例如大商所合约,报单如果开平方向填平今,那么即使这笔订单实际上是平掉了昨仓,成交里面的开平标志也会是平今,这是隐藏的一个小坑。
02
如何区分订单撤单时,是自己发起撤单还是被交易所拒单而导致的?
CTP中订单被撤单时,报单通知OnRtnOrder中的报单状态OrderStatus是已撤单THOST_FTDC_OST_Canceled ('5')。那么,如何区分订单到底是因为自己发出了撤单请求而撤的单,还是被交易所拒单而撤单的呢?
注:已撤单时可能有其他的报单状态OrderStatus,如THOST_FTDC_OST_PartTradedNotQueueing,这种情况下可认为都是自己发起撤单导致的。
第一种法子,是判断报单编号OrderSysID是否为空。如果不为空即已经产生了报单编号,说明这笔订单此前进入了交易所撮合队列中,所以此次撤单是被自己撤的单。否则是被交易所拒单了,或者订单还没有进入交易所就被你自己撤掉了。
第二种法子,是判断操作用户代码ActiveUserID是否为空。如果是自己发起的撤单,则ActiveUserID为你的账户名,否则它为空。
第三种法子,是判断报单提交状态OrderSubmitStatus。如果是自己发起的撤单,则撤单时OrderSubmitStatus为"已经接受"THOST_FTDC_OSS_Accepted ('3'),否则为"报单已经被拒绝"THOST_FTDC_OSS_InsertRejected ('4')。
03
订阅合约却收到了退订的响应?
如果行情API中,订阅合约(Subscribe*)却收到了退订(UnSubscribe*)的响应,则可能是你的行情API错误的连接到了交易前置地址上。
另一个可能是,你使用的头文件和库文件(dll, so等)它们各自的版本不一样,例如头文件的版本是6.3.15而库文件的版本是6.3.19。
04
API在断开连接时崩溃了?
低版本的CTP API在频繁与前置断连时确实有极低可能崩掉,不过高版本(since 6.3.19)的API,已经修复了这个问题了,所以换用较高版本的API即可解决问题。
也可以通过,进入非交易时间段则将API释放掉,临近交易时间时再创建API实例,从而避免这个问题。
另外,崩掉也有可能是你的程序编写有问题,例如在断开连接的响应OnFrontDisconnected()中,调用 Release()释放了API。正确的做法是,如果要释放API,需要在别的线程中进行,而不能在SPI函数中直接执行释放操作,而且,创建与释放API实例,最好是在同一个线程中。
05
结算单中为什么有乱码?
通过CTP的交易API的请求查询投资者结算结果ReqQrySettlementInfo接口,可以查询账户的结算单,结算单内容通过OnRspQrySettlementInfo接口返回。如果没有处理好,结算单中可能会有乱码。
首先需要知道,CTP中的字符串,都是使用GBK系的编码,不能使用UTF8等编码来解码,因此如果是在linux系统中查询结算单并显示,则可能由于终端编码等是UTF8不是GBK编码而显示为乱码,这个可以修改终端等使用的编码或者在程序中转换编码来解决。
其次,结算单内容较长,而OnRspQrySettlementInfo中的数据结构长度是可能不够存储结算单的,因此一般是分成多条来返回的。而连续的两条响应的结算内容正文Content 的衔接的地方,则会有编码处理的问题。响应中的结算内容,其实是char数组,因此,可能正好遇到有中文字符在两条响应交界处而被拆分的情况,所以,如果是每次收到一条查询结算单响应,就把它解码显示出来,遇到这种情况是就无法解析最后一个被拆成两半的中文字符了,乱码就在所难免了。
因此正确的处理方法是,用一个大的字符数组,汇总拼接存储各条响应中的结算内容字符。当收到全部的查询结算内容响应时,将这个字符数组进行解码,这时得到的就不会有乱码了。
如果用的是python等转换后的其他语言的API,则需要用类似的思路,用byte数组而非string数据结构来对应接受C++中的结算单内容字段,然后汇总拼接各个响应里面的结算单。
///请求查询投资者结算结果响应void OnRspQrySettlementInfo(CThostFtdcSettlementInfoField *pSettlementInfo, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
static char settlement[MAX_LEN] = { 0 };
static int tempIndex = 0;
strncpy(settlement + tempIndex, pSettlementInfo->Content, sizeof(pSettlementInfo->Content) - 1);
tempIndex += sizeof(pSettlementInfo->Content) - 1;
if(bIsLast)
{ //完成生成结算单,显示结算单
std::cout << settlement << std::endl;
}
}
关键词:
