0. 欢迎交流
更新时间:20190502
github: https://github.com/nicai0609/
1. 准备工作
和上文traderapi一致。此版本是在原先版本上的升级,解决了onfrontconnected回调的宕机问题。原先采用的是python2,现在升级为python3.7.2。另外关于SubscribeMarketData这个函数的C++二级指针也找到了更好的方法解决;还有解决了CTP返回中文字符的问题。
2. 通过Swig得到python接口文件
基础环境参考上篇traderapi。
新建文件thostmduserapi.i
,内容如下
%module(directors="1") thostmduserapi
%{
#include "ThostFtdcMdApi.h"
#include "iconv.h"
%}
%feature("director") CThostFtdcMdSpi;
%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;
%typemap(out) char[ANY], char[] {
if ($1) {
iconv_t cd = iconv_open("utf-8", "gb2312");
if (cd != reinterpret_cast<iconv_t>(-1)) {
char buf[4096] = {};
char **in = &$1;
char *out = buf;
size_t inlen = strlen($1), outlen = 4096;
if (iconv(cd, in, &inlen, &out, &outlen) != static_cast<size_t>(-1))
{
size_t size = outlen;
while (size && (buf[size - 1] == '\0')) --size;
resultobj = SWIG_FromCharPtrAndSize(buf, size);
}
iconv_close(cd);
}
}
}
%typemap(in) char *[] {
/* Check if is a list */
if (PyList_Check($input)) {
int size = PyList_Size($input);
int i = 0;
$1 = (char **) malloc((size+1)*sizeof(char *));
for (i = 0; i < size; i++) {
PyObject *o = PyList_GetItem($input, i);
if (PyString_Check(o)) {
$1[i] = PyString_AsString(PyList_GetItem($input, i));
} else {
free($1);
PyErr_SetString(PyExc_TypeError, "list must contain strings");
SWIG_fail;
}
}
$1[i] = 0;
} else {
PyErr_SetString(PyExc_TypeError, "not a list");
SWIG_fail;
}
}
// This cleans up the char ** array we malloc'd before the function call
%typemap(freearg) char ** {
free((char *) $1);
}
%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcMdApi.h"
这第二个typemap函数就是将python list转化为c++的二级指针。
在cmd
中切换到当前文件夹下,运行命令
swig -threads -c++ -python thostmduserapi.i
等到运行完成后,可以看到当前目录下生成了
thostmduserapi_wrap.h
thostmduserapi_wrap.cxx
thostmduserapi.py
3. 通过C++得到python可调用的pyd动态库
与上文traderapi中一致。
4. Python Demo
新建文件mduserapi_demo.py,注意文件同目录底下要有如下三个文件:
thostmduserapi.py
thostmduserapi.dll
_thostmduserapi.pyd
要注意demo中一点,SubscribeMarketData函数的参数1是python list,在list的每个合约前记得加’b’。详见demo。这个demo只需要改个CTP行情前置的地址就可以运行。订阅合约改成自己想要订阅的合约。
本demo实现登录成功后订阅两个合约行情的功能。完整的demo代码如下:
# -*- coding: utf-8 -*-
import thostmduserapi as mdapi
class CFtdcMdSpi(mdapi.CThostFtdcMdSpi):
tapi=''
def __init__(self,tapi):
mdapi.CThostFtdcMdSpi.__init__(self)
self.tapi=tapi
def OnFrontConnected(self):
print ("OnFrontConnected")
loginfield = mdapi.CThostFtdcReqUserLoginField()
loginfield.BrokerID="8000"
loginfield.UserID="000005"
loginfield.Password="123456"
loginfield.UserProductInfo="python dll"
self.tapi.ReqUserLogin(loginfield,0)
def OnRspUserLogin(self, *args):
print ("OnRspUserLogin")
rsploginfield=args[0]
rspinfofield=args[1]
print ("SessionID=",rsploginfield.SessionID)
print ("ErrorID=",rspinfofield.ErrorID)
print ("ErrorMsg=",rspinfofield.ErrorMsg)
ret=self.tapi.SubscribeMarketData([b"ru1905",b"rb1905"],2)
def OnRtnDepthMarketData(self, *args):
print ("OnRtnDepthMarketData")
field=args[0]
print ("InstrumentID=",field.InstrumentID)
print ("LastPrice=",field.LastPrice)
def OnRspSubMarketData(self, *args):
print ("OnRspSubMarketData")
field=args[0]
print ("InstrumentID=",field.InstrumentID)
rspinfofield=args[1]
print ("ErrorID=",rspinfofield.ErrorID)
print ("ErrorMsg=",rspinfofield.ErrorMsg)
def main():
mduserapi=mdapi.CThostFtdcMdApi_CreateFtdcMdApi()
mduserspi=CFtdcMdSpi(mduserapi)
mduserapi.RegisterFront("tcp://180.168.146.187:10031")
mduserapi.RegisterSpi(mduserspi)
mduserapi.Init()
mduserapi.Join()
if __name__ == '__main__':
main()
5. 常见问题
声明:仅是个人爱好编译,对此API引起的你的任何损失不负责任。