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

前言

目前上期技术CTP系统提供的API版本是C++版本,本文主要介绍Windows 64位平台下利用Swig工具将CTP C++接口mduser API转换为Python可调用的接口文件。

0. 欢迎交流

QQ群:446042777(澄明期货研究)

1. 准备工作

和上文CTP Python API 利用Swig 封装Windows版(traderapi)-CSDN博客一致。此版本是在原先版本上的升级,解决了onfrontconnected回调的宕机问题。

另外关于SubscribeMarketData这个函数的C++二级指针也找到了更好的方法解决;还有解决了CTP返回中文字符的问题。

本文使用的环境:CTP v6.7.8_20240918、Swig swigwin-4.3.0版本、Python 3.12.9、Visual Studio 2022

2. 通过Swig得到python接口文件

基础环境参考上篇traderapi文章
新建文件thostmduserapi.i,内容如下

%module(directors="1") thostmduserapi
%{
#include "ThostFtdcMdApi.h"
#include <codecvt>
#include <locale>
#include <vector>
#include <string>
using namespace std;
#ifdef _MSC_VER
const static locale g_loc("zh-CN");
#else    
const static locale g_loc("zh_CN.GB18030");
#endif
%}

%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[] {
    const std::string &gb2312($1);
    std::vector<wchar_t> wstr(gb2312.size());
    wchar_t* wstrEnd = nullptr;
    const char* gbEnd = nullptr;
    mbstate_t state = {};
    int res = use_facet<codecvt<wchar_t, char, mbstate_t> >
        (g_loc).in(state,
            gb2312.data(), gb2312.data() + gb2312.size(), gbEnd,
            wstr.data(), wstr.data() + wstr.size(), wstrEnd);
 
    if (codecvt_base::ok == res)
    {
        wstring_convert<codecvt_utf8<wchar_t>> cutf8;
        std::string result = cutf8.to_bytes(wstring(wstr.data(), wstrEnd));       
        resultobj = SWIG_FromCharPtrAndSize(result.c_str(), result.size()); 
    }
    else
    {
        std::string result;
        resultobj = SWIG_FromCharPtrAndSize(result.c_str(), result.size()); 
    }
}
%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

更多swig命令用法请在这里查看swig文档地址

等到运行完成后,可以看到当前目录下生成了

thostmduserapi_wrap.h
thostmduserapi_wrap.cxx
thostmduserapi.py

常见问题:

和mduserapi文章中一样,默认运行 swig -threads -c++ -python thostmduserapi.i 后可能会出现以下警告:

ThostFtdcMdApi.h(30) : Warning 514: Director base class CThostFtdcMdSpi has no virtual destructor.

问题原因:

警告 514 表示基类 CThostFtdcMdSpi 没有虚析构函数(virtual destructor)。在 C++ 中,如果一个类被设计为基类并可能被继承,通常需要将析构函数声明为虚的,以确保派生类对象在销毁时能够正确调用基类的析构函数。

解决方法

直接忽略警告或使用 SWIG 的 %ignore

根据网上结果,许多开发者在封装 CTP API 时也遇到了类似的警告,但最终程序仍然可以正常运行。

或者修改SWIG 的接口文件(.i 文件),可以在接口文件中使用 %ignore 来处理警告。在 .i 文件中添加以下内容:

%ignore CThostFtdcMdSpi::~CThostFtdcMdSpi;

这会告诉 SWIG 忽略 CThostFtdcMdSpi 的析构函数,从而避免警告。 

3. 通过C++得到python可调用的pyd动态库

与此文CTP Python API 利用Swig 封装Windows版(traderapi)-CSDN博客中一致。 

在Visual Studio中建立一个C++工程,建工程的步骤可参考CTP Windows版C++工程建立和编译(traderapi)-CSDN博客这篇文章,需要注意几点:1)工程选择dll类型,2)运行库选多线程(/MT)。然后将如下文件拷贝到工程文件夹下:

ThostFtdcMdApi.h

ThostFtdcUserApiDataType.h

ThostFtdcUserApiStruct.h

thostmduserapi_se.lib

thostmduserapi_wrap.cxx

thostmduserapi_wrap.h

项目目录及编译成功后如下图所示:

编译出dll文件位置如下图所示:

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="9999"
        loginfield.UserID="000001"
        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"ru2505",b"rb2505"],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. 常见问题

参考上篇文章CTP Python API 利用Swig 封装Windows版(traderapi)文末

6. 其他

thostmduserapi.py中添加2个方法

def CThostFtdcMdApi_CreateFtdcMdApi(*args) -> "CThostFtdcMdApi *":
    return _thostmduserapi.CThostFtdcMdApi_CreateFtdcMdApi(*args)

def CThostFtdcMdApi_GetApiVersion() -> "char const *":
    return _thostmduserapi.CThostFtdcMdApi_GetApiVersion()

7.编译好的文件参考:

https://github.com/Lumosylva/20240918_traderapi64_se_windowshttps://github.com/Lumosylva/20240918_traderapi64_se_windows

网速不好可访问下方国内地址:

GitCode - 全球开发者的开源社区,开源代码托管平台GitCode是面向全球开发者的开源社区,包括原创博客,开源代码托管,代码协作,项目管理等。与开发者社区互动,提升您的研发效率和质量。https://gitcode.com/mdd2012/20240918_traderapi64_se_windows

声明:

本API仅是个人爱好编译,仅为用户提供交易工具参考,对此API引起的你的任何损失不负责任,你应自行对交易工具负责,本API不对用户的交易结果承担任何责任,在任何情况下,不对因技术问题导致的用户损失承担责任。

上篇文章:

CTP Python API 利用Swig 封装Windows版(traderapi)_ctp python swig-CSDN博客
————————————————

感谢景色大佬,参考文章:

CTP Python API及Demo(利用Swig 封装)Windows版(mduserapi)_thostmduserapi-CSDN博客

Swig转换C++接口中文乱码解决方案_swig 中文乱码-CSDN博客

CTP JAVA API(JCTP)编译(利用Swig封装C++动态库)windows版-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值