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引起的你的任何損失不負責任。