前言:
目前上期技术CTP系统提供的API版本是C++
版本,本文主要介绍Windows 64位平台下利用Swig工具将CTP C++接口trader API转换为python可调用的接口。原先本文是基于python2,现在升级为基于python3.7.2。
更新时间:20191208
github: https://github.com/nicai0609/Python-CTPAPI
CTP API
点击下载。这里用的版本是6.3.11_20180109(现在官方升级了版本,此版本不提供下载了,请去官网下载最新版本v6.3.15_20190220),64位的API文件包清单如下:error.dtd
error.xml
ThostFtdcMdApi.h
ThostFtdcTraderApi.h
ThostFtdcUserApiDataType.h
ThostFtdcUserApiStruct.h
thostmduserapi.dll
thostmduserapi.lib
thosttraderapi.dll
thosttraderapi.lib
swigwin-4.0.0
版本,点击下载。3.7.2
版本,如果自用用到别的版本,下面步骤一致。如果别的python版本有问题,可以与我联系。libiconv
库。这个库主要适用于字节编码转换,因为CTP的中文是GB2312编码,转换为UTF-8编码,适合python输出。这个库我已经编译成静态库了,如果不想自己编译,可以去群里下载或者在CTP JAVA API的github项目里下载。本文一开始采用的是第三方库libiconv转换,下面也以libiconv为例继续。C++11库中已有字节编码转换方式,采用这种方式可以不用libiconv
库,下面和libiconv
相关的都可以略去,见《Swig转换C++接口中文乱码解决方案》在刚刚下载得到的API文件夹20180109_tradeapi64_windows
内,新建文件thosttraderapi.i
,内容如下
%module(directors="1") thosttraderapi
%{
#include "ThostFtdcTraderApi.h"
#include "iconv.h"
%}
%typemap(out) char[ANY], char[] {
if ($1) {
iconv_t cd = iconv_open("utf-8", "gb2312");
if (cd != reinterpret_cast(-1)) {
char buf[4096] = {};
char **in = &$1;
char *out = buf;
size_t inlen = strlen($1), outlen = 4096;
if (iconv(cd, (const char **)in, &inlen, &out, &outlen) != static_cast(-1))
{
size_t size = outlen;
while (size && (buf[size - 1] == '\0')) --size;
resultobj = SWIG_FromCharPtrAndSize(buf, size);
}
iconv_close(cd);
}
}
}
%feature("director") CThostFtdcTraderSpi;
%ignore THOST_FTDC_VTC_BankBankToFuture;
%ignore THOST_FTDC_VTC_BankFutureToBank;
%ignore THOST_FTDC_VTC_FutureBankToFuture;
%ignore THOST_FTDC_VTC_FutureFutureToBank;
%ignore THOST_FTDC_FTC_BankLaunchBankToBroker;
%ignore THOST_FTDC_FTC_BrokerLaunchBankToBroker;
%ignore THOST_FTDC_FTC_BankLaunchBrokerToBank;
%ignore THOST_FTDC_FTC_BrokerLaunchBrokerToBank;
%feature("director") CThostFtdcTraderSpi;
%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcTraderApi.h"
这是一个接口文件,用于告诉swig为哪些类和方法创建接口。打开windows cmd
工具,cd
到当前目录\20180109_tradeapi_windows
下。 在cmd
中运行命令
swig -threads -py3 -c++ -python thosttraderapi.i
等到运行完成后,可以看到当前目录下生成了
thosttraderapi_wrap.h
thosttraderapi_wrap.cxx
thosttradeapi.py
.h
和.cx
文件是用于包装原来C++
接口的文件,下面要用。.py
文件是python调用方法的接口文件。
在当前文件夹下建立一个C++工程,工程的应用程序类型选DLL
,工程名为_thosttraderapi
(建工程的步骤也可参考https://blog.csdn.net/pjjing/article/details/53186394这篇文章第3步开头),需要注意几点:1)工程建64位dll类型,2)运行库选多线程(/MT)。然后将如下文件拷贝到_thosttraderapi\
文件夹下:
ThostFtdcTraderApi.h
ThostFtdcUserApiDataType.h
ThostFtdcUserApiStruct.h
thosttraderapi.lib
thosttraderapi_wrap.cxx
thosttraderapi_wrap.h
libiconv.lib
iconv.h
在c++
工程中添加现有项,将这些文件全部添加到工程中去。下面还要做几步:
C++
附加包含目录。我的路径是C:\Python37\include;
,C++
附加包含目录在工程-属性-配置属性-c/c++
处。C:\Python37\libs\python37.lib;
,附加依赖项在工程-属性-配置属性-链接器-输入处。这样全部完成之后,选择Release版本,我们按F7编译,在\_thosttraderapi\Release
目录底下可见_thosttraderapi.dll
动态库文件,说明编译成功,将其重命名为_thosttraderapi.pyd
,这样CTP Python API
就编译成功了。
如果编译出现一些问题,可以百度解决。可能涉及到要修改pyconfig.h,object.h,Python.h
三个文件。
新建文件traderapi_demo.py,注意文件同目录底下要有如下三个文件:
thosttradeapi.py
thosttraderapi.dll
_thosttradeapi.pyd
本demo实现登录成功后报单,收报单回报的功能。完整的demo代码如下:
# -*- coding: utf-8 -*-
import thosttraderapi as api
#Addr
FrontAddr="tcp://180.168.146.187:10100"
#FrontAddr="tcp://180.168.146.187:10130"
#LoginInfo
BROKERID="9999"
USERID="00001"
PASSWORD="00001"
#OrderInfo
INSTRUMENTID="rb1909"
PRICE=3200
VOLUME=1
DIRECTION=api.THOST_FTDC_D_Sell
#DIRECTION=api.THOST_FTDC_D_Buy
#open
OFFSET="0"
#close
#OFFSET="1"
def ReqorderfieldInsert(tradeapi):
print ("ReqOrderInsert Start")
orderfield=api.CThostFtdcInputOrderField()
orderfield.BrokerID=BROKERID
orderfield.InstrumentID=INSTRUMENTID
orderfield.UserID=USERID
orderfield.InvestorID=USERID
orderfield.Direction=DIRECTION
orderfield.LimitPrice=PRICE
orderfield.VolumeTotalOriginal=VOLUME
orderfield.OrderPriceType=api.THOST_FTDC_OPT_LimitPrice
orderfield.ContingentCondition = api.THOST_FTDC_CC_Immediately
orderfield.TimeCondition = api.THOST_FTDC_TC_GFD
orderfield.VolumeCondition = api.THOST_FTDC_VC_AV
orderfield.CombHedgeFlag="1"
orderfield.CombOffsetFlag=OFFSET
orderfield.GTDDate=""
orderfield.OrderRef="1"
orderfield.MinVolume = 0
orderfield.ForceCloseReason = api.THOST_FTDC_FCC_NotForceClose
orderfield.IsAutoSuspend = 0
tradeapi.ReqOrderInsert(orderfield,0)
print ("ReqOrderInsert End")
class CTradeSpi(api.CThostFtdcTraderSpi):
tapi=''
def __init__(self,tapi):
api.CThostFtdcTraderSpi.__init__(self)
self.tapi=tapi
def OnFrontConnected(self) -> "void":
print ("OnFrontConnected")
loginfield = api.CThostFtdcReqUserLoginField()
loginfield.BrokerID=BROKERID
loginfield.UserID=USERID
loginfield.Password=PASSWORD
loginfield.UserProductInfo="python dll"
self.tapi.ReqUserLogin(loginfield,0)
print ("send login ok")
def OnRspUserLogin(self, pRspUserLogin: 'CThostFtdcRspUserLoginField', pRspInfo: 'CThostFtdcRspInfoField', nRequestID: 'int', bIsLast: 'bool') -> "void":
print ("OnRspUserLogin")
print ("TradingDay=",pRspUserLogin.TradingDay)
print ("SessionID=",pRspUserLogin.SessionID)
print ("ErrorID=",pRspInfo.ErrorID)
print ("ErrorMsg=",pRspInfo.ErrorMsg)
qryinfofield = api.CThostFtdcQrySettlementInfoField()
qryinfofield.BrokerID=BROKERID
qryinfofield.InvestorID=USERID
qryinfofield.TradingDay=pRspUserLogin.TradingDay
self.tapi.ReqQrySettlementInfo(qryinfofield,0)
print ("send ReqQrySettlementInfo ok")
def OnRspQrySettlementInfo(self, pSettlementInfo: 'CThostFtdcSettlementInfoField', pRspInfo: 'CThostFtdcRspInfoField', nRequestID: 'int', bIsLast: 'bool') -> "void":
print ("OnRspQrySettlementInfo")
if pSettlementInfo is not None :
print ("content:",pSettlementInfo.Content)
else :
print ("content null")
if bIsLast :
pSettlementInfoConfirm=api.CThostFtdcSettlementInfoConfirmField()
pSettlementInfoConfirm.BrokerID=BROKERID
pSettlementInfoConfirm.InvestorID=USERID
self.tapi.ReqSettlementInfoConfirm(pSettlementInfoConfirm,0)
print ("send ReqSettlementInfoConfirm ok")
def OnRspSettlementInfoConfirm(self, pSettlementInfoConfirm: 'CThostFtdcSettlementInfoConfirmField', pRspInfo: 'CThostFtdcRspInfoField', nRequestID: 'int', bIsLast: 'bool') -> "void":
print ("OnRspSettlementInfoConfirm")
print ("ErrorID=",pRspInfo.ErrorID)
print ("ErrorMsg=",pRspInfo.ErrorMsg)
ReqorderfieldInsert(self.tapi)
print ("send ReqorderfieldInsert ok")
def OnRtnOrder(self, pOrder: 'CThostFtdcOrderField') -> "void":
print ("OnRtnOrder")
print ("OrderStatus=",pOrder.OrderStatus)
print ("StatusMsg=",pOrder.StatusMsg)
print ("LimitPrice=",pOrder.LimitPrice)
def OnRspOrderInsert(self, pInputOrder: 'CThostFtdcInputOrderField', pRspInfo: 'CThostFtdcRspInfoField', nRequestID: 'int', bIsLast: 'bool') -> "void":
print ("OnRspOrderInsert")
print ("ErrorID=",pRspInfo.ErrorID)
print ("ErrorMsg=",pRspInfo.ErrorMsg)
def main():
tradeapi=api.CThostFtdcTraderApi_CreateFtdcTraderApi()
tradespi=CTradeSpi(tradeapi)
tradeapi.RegisterSpi(tradespi)
tradeapi.SubscribePrivateTopic(api.THOST_TERT_QUICK)
tradeapi.SubscribePublicTopic(api.THOST_TERT_QUICK)
tradeapi.RegisterFront(FrontAddr)
tradeapi.Init()
tradeapi.Join()
if __name__ == '__main__':
main()
感谢网友 weixin_41707895 整理
解决方法:将i文件中,if (iconv(cd, (const char **)in, &inlen, &out, &outlen) != static_cast
解决方法:项目属性->配置属性->C/C+±>预处理器->预处理器定义中添加:_CRT_SECURE_NO_WARNINGS
解决方法:选择编译64位库时,vs中需要如下设置:解决方案属性->配置属性->将平台选为X64->配置管理器->选择X64平台 右键项目清理即可
解决方法:项目属性->配置属性->高级->全程序优化改为无全程序优化
解决方法:项目属性->配置属性->C/C+±>命令行->其它选项中键入/bigobj
解决方法:项目属性->配置属性->链接器->附加依赖项中添加legacy_stdio_definitions.lib
声明:仅是个人爱好编译,对此API引起的你的任何损失不负责任。