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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章