CTP柜台在期货领域具有举足轻重的地位,其提供的API以代码风格的规范著称。近期实现了一下对其接口的测试,使用起来还是比较简单清晰的,发现的一个问题就是接口与字段太多了,对使用者的理解增加了不少难度,好在对大多数使用者来说,不需要搞清楚所有的接口和字段。
因为CTP接口的规范性,我这里将其接口抽象成了了几种元素,进一步通过python脚本自动生成了一些代码,以达到对所有回调函数的所有字段进行日志记录的功能。同时对一些关键的接口进行了测试。因为篇幅限制,这里仅列出部分接口的代码实现,如果感兴趣可以移步 GitHub 获取完整代码。
CTP 的SPI类其回调函数主要分为五类,On、OnRsp、OnRspQry、OnErrRtn、OnRtn:
以On开头的回调函数参数通常是一些简单的类型或者无参数;
以OnRsp和OnRspQry开头的回调函数参数,包含 一个Field 结构,加一个固定的 CThostFtdcRspInfoField 结构,还有两个简单的参数 int nRequestID 和 bool bIsLast;
以OnErrRtn开头的回调函数参数,包含 一个Field 结构,加一个固定的 CThostFtdcRspInfoField 结构;
以OnRtn开头的回调函数参数,仅包含 一个Field 结构。
一些元素的抽象如下所示,
<?xml version="1.0" encoding="utf-8"?>
<root>
<fields>
<field name="RspInfo">
<item name="ErrorID" type="int"/>
<item name="ErrorMsg" type="string"/>
</field>
<field name="RspAuthenticate">
<item name="BrokerID" type="string"/>
<item name="UserID" type="string"/>
<item name="UserProductInfo" type="string"/>
<item name="AppID" type="string"/>
<item name="AppType" type="char"/>
</field>
<field name="RspUserLogin">
<item name="TradingDay" type="string"/>
<item name="LoginTime" type="string"/>
<item name="BrokerID" type="string"/>
<item name="UserID" type="string"/>
<item name="SystemName" type="string"/>
<item name="FrontID" type="int"/>
<item name="SessionID" type="int"/>
<item name="MaxOrderRef" type="string"/>
<item name="SHFETime" type="string"/>
<item name="DCETime" type="string"/>
<item name="CZCETime" type="string"/>
<item name="FFEXTime" type="string"/>
<item name="INETime" type="string"/>
</field>
<field name="UserLogout">
<item name="BrokerID" type="string"/>
<item name="UserID" type="string"/>
</field>
<field name="UserPasswordUpdate">
<item name="BrokerID" type="string"/>
<item name="UserID" type="string"/>
<item name="OldPassword" type="string"/>
<item name="NewPassword" type="string"/>
</field>
<field name="TradingAccountPasswordUpdate">
<item name="BrokerID" type="string"/>
<item name="AccountID" type="string"/>
<item name="OldPassword" type="string"/>
<item name="NewPassword" type="string"/>
<item name="CurrencyID" type="string"/>
</field>
<field name="RspUserAuthMethod">
<item name="UsableAuthMethod" type="int"/>
</field>
<field name="RspGenUserCaptcha">
<item name="BrokerID" type="string"/>
<item name="UserID" type="string"/>
<item name="CaptchaInfoLen" type="int"/>
<item name="CaptchaInfo" type="string"/>
</field>
...
</fields>
<spi>
<func name="OnFrontConnected" retrun="void" type="On" sequence="0"/>
<func name="OnFrontDisconnected" retrun="void" type="On" sequence="1">
<param type="int" name="nReason"/>
</func>
<func name="OnHeartBeatWarning" retrun="void" type="On" sequence="2">
<param type="int" name="nTimeLapse"/>
</func>
<func name="OnRspAuthenticate" retrun="void" type="OnRsp" sequence="3">
<param type="CThostFtdcRspAuthenticateField" name="RspAuthenticate"/>
</func>
<func name="OnRspUserLogin" retrun="void" type="OnRsp" sequence="4">
<param type="CThostFtdcRspUserLoginField" name="RspUserLogin"/>
</func>
<func name="OnRspUserLogout" retrun="void" type="OnRsp" sequence="5">
<param type="CThostFtdcUserLogoutField" name="UserLogout"/>
</func>
<func name="OnRspUserPasswordUpdate" retrun="void" type="OnRsp" sequence="6" >
<param type="CThostFtdcUserPasswordUpdateField" name="UserPasswordUpdate"/>
</func>
<func name="OnRspTradingAccountPasswordUpdate" retrun="void" type="OnRsp" sequence="7">
<param type="CThostFtdcTradingAccountPasswordUpdateField" name="TradingAccountPasswordUpdate"/>
</func>
<func name="OnRspUserAuthMethod" retrun="void" type="OnRsp" sequence="8">
<param type="CThostFtdcRspUserAuthMethodField" name="RspUserAuthMethod"/>
</func>
<func name="OnRspGenUserCaptcha" retrun="void" type="OnRsp" sequence="9">
<param type="CThostFtdcRspGenUserCaptchaField" name="RspGenUserCaptcha"/>
</func>
<func name="OnRspGenUserText" retrun="void" type="OnRsp" sequence="10">
<param type="CThostFtdcRspGenUserTextField" name="RspGenUserText"/>
</func>
<func name="OnRspOrderInsert" retrun="void" type="OnRsp" sequence="11">
<param type="CThostFtdcInputOrderField" name="InputOrder"/>
</func>
<func name="OnRspParkedOrderInsert" retrun="void" type="OnRsp" sequence="12">
<param type="CThostFtdcParkedOrderField" name="ParkedOrder"/>
</func>
<func name="OnRspParkedOrderAction" retrun="void" type="OnRsp" sequence="13">
<param type="CThostFtdcParkedOrderActionField" name="ParkedOrderAction"/>
</func>
<func name="OnRspOrderAction" retrun="void" type="OnRsp" sequence="14">
<param type="CThostFtdcInputOrderActionField" name="InputOrderAction"/>
</func>
<func name="OnRspQueryMaxOrderVolume" retrun="void" type="OnRsp" sequence="15">
<param type="CThostFtdcQueryMaxOrderVolumeField" name="QueryMaxOrderVolume"/>
</func>
<func name="OnRspSettlementInfoConfirm" retrun="void" type="OnRsp" sequence="16">
<param type="CThostFtdcSettlementInfoConfirmField" name="SettlementInfoConfirm"/>
</func>
<func name="OnRspRemoveParkedOrder" retrun="void" type="OnRsp" sequence="17">
<param type="CThostFtdcRemoveParkedOrderField" name="RemoveParkedOrder"/>
</func>
<func name="OnRspRemoveParkedOrderAction" retrun="void" type="OnRsp" sequence="18">
<param type="CThostFtdcRemoveParkedOrderActionField" name="RemoveParkedOrderAction"/>
</func>
<func name="OnRspExecOrderInsert" retrun="void" type="OnRsp" sequence="19">
<param type="CThostFtdcInputExecOrderField" name="InputExecOrder"/>
</func>
<func name="OnRspExecOrderAction" retrun="void" type="OnRsp" sequence="20">
<param type="CThostFtdcInputExecOrderField" name="InputExecOrderAction"/>
</func>
...
</spi>
</root>
对所有Field生成写日志的函数代码:
StructWriteLogFunc.cpp.tpl
#include "StructWriteLogFunc.h"
#include "Logger.h"
!!types={}!!
!!types['char']='c'!!
!!types['short']='d'!!
!!types['int']='d'!!
!!types['double']='f'!!
!!types['string']='s'!!
!!entry fields!!
!!travel!!
void Write!!@name!!(CThostFtdc!!@name!!Field* !!@name!!)
{
!!fieldName=@name!!
if(!!@name!!)
{
WRITE_LOG(LogLevel::Info, "CThostFtdc!!@name!!Field: !!travel!!!!currType=types[@type]!!!!if $pumpid != '1':!!!!inc indent!!, !!dec indent!!!!@name!![%!!$currType!!]!!leave!!",
!!travel!!!!if $pumpid != '1':!!!!inc indent!!, !!dec indent!!!!$fieldName!!->!!@name!!!!leave!!);
}
}
!!leave!!
!!leave!!
生成后部分代码如下:
StructWriteLogFunc.cpp
#include "StructWriteLogFunc.h"
#include "Logger.h"
void WriteRspInfo(CThostFtdcRspInfoField* RspInfo)
{
if(RspInfo)
{
WRITE_LOG(LogLevel::Info, "CThostFtdcRspInfoField: ErrorID[%d], ErrorMsg[%s]",
RspInfo->ErrorID, RspInfo->ErrorMsg);
}
}
void WriteRspAuthenticate(CThostFtdcRspAuthenticateField* RspAuthenticate)
{
if(RspAuthenticate)
{
WRITE_LOG(LogLevel::Info, "CThostFtdcRspAuthenticateField: BrokerID[%s], UserID[%s], UserProductInfo[%s], AppID[%s], AppType[%c]",
RspAuthenticate->BrokerID, RspAuthenticate->UserID, RspAuthenticate->UserProductInfo, RspAuthenticate->AppID, RspAuthenticate->AppType);
}
}
void WriteRspUserLogin(CThostFtdcRspUserLoginField* RspUserLogin)
{
if(RspUserLogin)
{
WRITE_LOG(LogLevel::