CTP Python API及Demo(利用Swig 封装)Windows版(mduserapi)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/yishuihan1212/article/details/77338482

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引起的你的任何损失不负责任。

猜你喜欢

转载自blog.csdn.net/yishuihan1212/article/details/77338482