Swig转换CTPAPI为Python攻略

C++ CTPAPI 是上期技术提供一个交易接口,用于连接柜台进行交易。由于 C++ 本身的难度以及不便于进行一般性测试,得益于热心网友努力,出现了Python版本的 CTPAPI。

这里参考 景色 的文章,对这一流程在不同平台 进行了整理,同时已将转换过程涉及到的文件上传至 github 仓库 github.com/openctp/ctp... 。 同时也提供了也提供了更完善友好的 python openctp-ctp 库以对接 CTPAPI 柜台, 欢迎使用。

Windows平台转换

环境准备

编译前,需要准备好依赖的各种环境。 这是我的本地环境,仅供参考,也可以根据自己的本地环境做适当调整。

创建DLL项目

分别创建 _thosttraderapi_thostmduserapi 项目
PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir

PowerShell 复制代码
PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        08/01/2024     14:30                _thostmduserapi
d-----        08/01/2024     10:46                _thosttraderapi

解压CTPAPI-6.7.0

解压后进入到路径 v6.7.0_20230209_winApi\traderapi\20230209_traderapi64_se_windows
PS E:\v6.7.0_20230209_winApi\traderapi\20230209_traderapi64_se_windows> dir

PowerShell 复制代码
PS E:\projects\ctp-resources\6.7.0\v6.7.0_20230209_winApi\traderapi\20230209_traderapi64_se_windows> dir
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        28/06/2022     11:59            184 error.dtd
-a----        30/01/2023     14:42          21886 error.xml
-a----        09/02/2023     10:02           5906 ThostFtdcMdApi.h
-a----        09/02/2023     10:00          41046 ThostFtdcTraderApi.h
-a----        09/02/2023     10:02         267340 ThostFtdcUserApiDataType.h
-a----        09/02/2023     10:02         284938 ThostFtdcUserApiStruct.h
-a----        09/02/2023     10:04        2960384 thostmduserapi_se.dll
-a----        09/02/2023     10:04           3822 thostmduserapi_se.lib
-a----        09/02/2023     10:02        3346944 thosttraderapi_se.dll
-a----        09/02/2023     10:02           3960 thosttraderapi_se.lib

将头文件、库文件复制到 E:\projects\ctpapi2python-swig-VisualStudio\vs2022 路径下
PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir

PowerShell 复制代码
(base) PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        08/01/2024     10:46                _thosttraderapi
d-----        08/01/2024     14:30                _thostmduserapi
-a----        09/02/2023     10:02           5906 ThostFtdcMdApi.h
-a----        09/02/2023     10:00          41046 ThostFtdcTraderApi.h
-a----        09/02/2023     10:02         267340 ThostFtdcUserApiDataType.h
-a----        09/02/2023     10:02         284938 ThostFtdcUserApiStruct.h
-a----        09/02/2023     10:04        2960384 thostmduserapi_se.dll
-a----        09/02/2023     10:04           3822 thostmduserapi_se.lib
-a----        09/02/2023     10:02        3346944 thosttraderapi_se.dll
-a----        09/02/2023     10:02           3960 thosttraderapi_se.lib

添加 .i 文件

thosttraderapi.i thostmduserapi.i 文件也放到 E:\projects\ctpapi2python-swig-VisualStudio\vs2022 路径下
thosttraderapi.i

text 复制代码
%module(directors="1") thosttraderapi
%{
#include "ThostFtdcTraderApi.h"
#include "iconv.h"
%}
%typemap(out) char[ANY], char[] {
if ($1) {
iconv_t cd = iconv_open("utf-8", "GBK");
if (cd != (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) != (size_t)(-1))
            {
                $result = SWIG_FromCharPtrAndSize(buf, sizeof buf - outlen);
            }
            iconv_close(cd);
        }
    }
}
%feature("python:annotations", "c");
%feature("director") CThostFtdcTraderSpi;
%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;

%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcTraderApi.h" 

thostmduserapi.i

text 复制代码
%module(directors="1") thostmduserapi
%{
#include "ThostFtdcMdApi.h"
#include "iconv.h"
%}

%feature("python:annotations", "c");
%feature("director") CThostFtdcMdSpi;

%typemap(out) char[ANY], char[] {
if ($1) {
iconv_t cd = iconv_open("utf-8", "GBK");
if (cd != (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) != (size_t)(-1))
            {
                $result = SWIG_FromCharPtrAndSize(buf, sizeof buf - outlen);
            }
            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);
}

%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;

%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcMdApi.h"

PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir

PowerShell 复制代码
PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        08/01/2024     14:30                _thostmduserapi
d-----        08/01/2024     10:46                _thosttraderapi
-a----        09/02/2023     10:02           5906 ThostFtdcMdApi.h
-a----        09/02/2023     10:00          41046 ThostFtdcTraderApi.h
-a----        09/02/2023     10:02         267340 ThostFtdcUserApiDataType.h
-a----        09/02/2023     10:02         284938 ThostFtdcUserApiStruct.h
-a----        08/01/2024     14:25           1752 thostmduserapi.i
-a----        09/02/2023     10:04        2960384 thostmduserapi_se.dll
-a----        09/02/2023     10:04           3822 thostmduserapi_se.lib
-a----        08/01/2024     14:25           1110 thosttraderapi.i
-a----        09/02/2023     10:02        3346944 thosttraderapi_se.dll
-a----        09/02/2023     10:02           3960 thosttraderapi_se.lib

添加iconv库

Python3 统一使用 UTF-8 编码, CTPAPI使用 GBK 编码, 需要 libiconv 进行编码转换
PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir

PowerShell 复制代码
PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        08/01/2024     14:30                _thostmduserapi
d-----        08/01/2024     10:46                _thosttraderapi
-a----        08/01/2024     10:34           9511 iconv.h
-a----        08/01/2024     10:34        1094144 libiconv.dll
-a----        09/02/2023     10:02           5906 ThostFtdcMdApi.h
-a----        09/02/2023     10:00          41046 ThostFtdcTraderApi.h
-a----        09/02/2023     10:02         267340 ThostFtdcUserApiDataType.h
-a----        09/02/2023     10:02         284938 ThostFtdcUserApiStruct.h
-a----        08/01/2024     14:25           1752 thostmduserapi.i
-a----        09/02/2023     10:04        2960384 thostmduserapi_se.dll
-a----        09/02/2023     10:04           3822 thostmduserapi_se.lib
-a----        08/01/2024     14:25           1110 thosttraderapi.i
-a----        09/02/2023     10:02        3346944 thosttraderapi_se.dll
-a----        09/02/2023     10:02           3960 thosttraderapi_se.lib

swig根据 .i 文件生成 python接口文件和wrap文件

PowerShell 复制代码
swig -threads -c++ -python thosttraderapi.i
swig -threads -c++ -python thostmduserapi.i

thosttraderapi 生成3个新文件:

text 复制代码
thosttraderapi.py
thosttraderapi_wrap.h
thosttraderapi_wrap.cxx

thostmduserapi 生成3个新文件:

text 复制代码
thostmduserapi.py
thostmduserapi_wrap.h
thostmduserapi_wrap.cxx

PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> dir

PowerShell 复制代码
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        08/01/2024     14:30                _thostmduserapi
d-----        08/01/2024     10:46                _thosttraderapi
-a----        08/01/2024     10:34           9511 iconv.h
-a----        08/01/2024     10:34        1094144 libiconv.dll
-a----        09/02/2023     10:02           5906 ThostFtdcMdApi.h
-a----        09/02/2023     10:00          41046 ThostFtdcTraderApi.h
-a----        09/02/2023     10:02         267340 ThostFtdcUserApiDataType.h
-a----        09/02/2023     10:02         284938 ThostFtdcUserApiStruct.h
-a----        08/01/2024     14:25           1752 thostmduserapi.i
-a----        08/01/2024     15:00        1188716 thostmduserapi.py
-a----        09/02/2023     10:04        2960384 thostmduserapi_se.dll
-a----        09/02/2023     10:04           3822 thostmduserapi_se.lib
-a----        08/01/2024     15:00       15030685 thostmduserapi_wrap.cxx
-a----        08/01/2024     15:00           3365 thostmduserapi_wrap.h
-a----        08/01/2024     14:25           1110 thosttraderapi.i
-a----        08/01/2024     15:00        1246410 thosttraderapi.py
-a----        09/02/2023     10:02        3346944 thosttraderapi_se.dll
-a----        09/02/2023     10:02           3960 thosttraderapi_se.lib
-a----        08/01/2024     15:00       15745002 thosttraderapi_wrap.cxx
-a----        08/01/2024     15:00          20855 thosttraderapi_wrap.h

_thosttraderapi 项目配置

  1. 添加头文件

    text 复制代码
    iconv.h
    ThostFtdcTraderApi.h
    ThostFtdcUserApiDataType.h
    ThostFtdcUserApiStruct.h
    thosttraderapi_wrap.cxx
  2. 添加源文件 thosttraderapi_wrap.cxx

  3. 添加库文件

  4. 附加包含目录

  5. 附加库目录

  6. 附加依赖项

  7. 不使用预编译头

  8. 添加编译项 /bigobj

  9. 运行库 /MT

  10. 选择平台 Release x64

  11. 构建生成 dll 文件

    _thosttraderapi\x64\Release 路径下会生成一个 _thosttraderapi.dll 文件, 将 _thosttraderapi.dll 重命名为 _thosttraderapi.pyd 并移动至路径 E:\projects\ctpapi2python-swig-VisualStudio\vs2022

    text 复制代码
    已启动重新生成...
    1>------ 已启动全部重新生成: 项目: _thosttraderapi, 配置: Release x64 ------
    1>pch.cpp
    1>thosttraderapi_wrap.cxx
    1>E:\projects\ctpapi2python-swig-VisualStudio\vs2022\ThostFtdcUserApiDataType.h(3706,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
    1>E:\projects\ctpapi2python-swig-VisualStudio\vs2022\ThostFtdcUserApiDataType.h(4468,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
    1>E:\projects\ctpapi2python-swig-VisualStudio\vs2022\iconv.h(1,1): warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
    1>dllmain.cpp
    1>  正在创建库 E:\projects\ctpapi2python-swig-VisualStudio\vs2022\_thosttraderapi\x64\Release\_thosttraderapi.lib 和对象 E:\projects\ctpapi2python-swig-VisualStudio\vs2022\_thosttraderapi\x64\Release\_thosttraderapi.exp
    1>正在生成代码
    1>Previous IPDB not found, fall back to full compilation.
    1>All 11713 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
    1>已完成代码的生成
    1>_thosttraderapi.vcxproj -> E:\projects\ctpapi2python-swig-VisualStudio\vs2022\_thosttraderapi\x64\Release\_thosttraderapi.dll
    1>已完成生成项目"_thosttraderapi.vcxproj"的操作。
    ========== "全部重新生成": 1 成功,0 失败,0已跳过 ==========
    ========= 重新生成 开始于 20:23,并花费了 16.648 秒 ==========

_thostmduserapi 项目配置

_thosttraderapi 的配置逻辑一致, 区别如下:

  1. 添加头文件

    text 复制代码
    iconv.h
    ThostFtdcTraderApi.h
    ThostFtdcUserApiDataType.h
    ThostFtdcUserApiStruct.h
    thostmduserapi_wrap.cxx
  2. 添加源文件 thostmduserapi_wrap.cxx

  3. 构建生成 dll 文件 在 _thostmduserapi\x64\Release 路径下会生成一个 _thostmduserapi.dll 文件, 将 _thostmduserapi.dll 重命名为 _thostmduserapi.pyd 并移动至路径 E:\projects\ctpapi2python-swig-VisualStudio\vs2022

测试交易、行情接口

交易接口、行情接口 前置地址,参考 openctp监控 SimNow

  1. 交易接口

    测试需要用到文件:

    text 复制代码
    demo_td.py
    iconv.dll
    charset.dll
    msvcp140.dll
    thosttraderapi_se.dll
    _thosttraderapi.pyd
    thosttraderapi.py

    demo_td.py

    python 复制代码
    import sys
    import time
    import os
    
    import thosttraderapi as tdapi
    
    # 前置地址 参考 http://121.37.80.177:50080/detail.html  SimNow
    FrontAddr = ""
    
    # 登录信息 需要在 SimNow https://www.simnow.com.cn/ 平台注册账号
    userid = ""
    password = ""
    brokerid = "9999"
    authcode = "0000000000000000"
    appid = "simnow_client_test"
    
    
    class CTdSpiImpl(tdapi.CThostFtdcTraderSpi):
    """交易回调实现类"""
    
        def __init__(self):
            super().__init__()
    
            flow_path = os.path.join(os.getcwd(), "flow_logs", userid)
            if not os.path.exists(flow_path):
                os.makedirs(flow_path)
            flow_path = os.path.join(flow_path, userid)
            self._api: tdapi.CThostFtdcTraderApi = (
                tdapi.CThostFtdcTraderApi.CreateFtdcTraderApi(flow_path)
            )
    
            print("CTP交易API版本号:", self._api.GetApiVersion())
    
            # 注册交易前置
            self._api.RegisterFront(FrontAddr)
            # 注册交易回调实例
            self._api.RegisterSpi(self)
            # 订阅私有流
            self._api.SubscribePrivateTopic(tdapi.THOST_TERT_QUICK)
            # 订阅公有流
            self._api.SubscribePublicTopic(tdapi.THOST_TERT_QUICK)
            # 初始化交易实例
            self._api.Init()
    
        def OnFrontConnected(self):
            """交易前置连接成功"""
            print("交易前置连接成功")
            self.authenticate()
    
        def OnFrontDisconnected(self, nReason: int):
            """交易前置连接断开"""
            print("交易前置连接断开: nReason=", nReason)
    
        def authenticate(self):
            """认证 demo"""
            print("> 认证请求")
            _req = tdapi.CThostFtdcReqAuthenticateField()
            _req.BrokerID = brokerid
            _req.UserID = userid
            _req.AppID = appid
            _req.AuthCode = authcode
            self._api.ReqAuthenticate(_req, 0)
    
        def OnRspAuthenticate(
            self,
            pRspAuthenticateField: tdapi.CThostFtdcRspAuthenticateField,
            pRspInfo: tdapi.CThostFtdcRspInfoField,
            nRequestID: int,
            bIsLast: bool,
        ):
            """客户端认证响应"""
            if pRspInfo:
                print("认证响应", pRspInfo.ErrorID, pRspInfo.ErrorMsg)
            if pRspInfo and pRspInfo.ErrorID != 0:
                return
    
            # 登录
            self.login()
    
        def login(self):
            """登录 demo"""
            print("> 登录请求")
    
            _req = tdapi.CThostFtdcReqUserLoginField()
            _req.BrokerID = brokerid
            _req.UserID = userid
            _req.Password = password
            if sys.platform == "darwin":
                self._api.ReqUserLogin(_req, 0, 0, "")
            else:
                self._api.ReqUserLogin(_req, 0)
    
        def OnRspUserLogin(
            self,
            pRspUserLogin: tdapi.CThostFtdcRspUserLoginField,
            pRspInfo: tdapi.CThostFtdcRspInfoField,
            nRequestID: int,
            bIsLast: bool,
        ):
            """登录响应"""
            if pRspInfo:
                print("登录响应", pRspInfo.ErrorID, pRspInfo.ErrorMsg)
    
    
    if __name__ == "__main__":
        if len(sys.argv) != 4:
            print("Usage:    python demo_td <frontaddr> <userid> <password>")
            exit(-1)
    
        FrontAddr = sys.argv[1]
        userid = sys.argv[2]
        password = sys.argv[3]
    
        spi = CTdSpiImpl()
        time.sleep(1)
        input("################# 按任意键退出 \n")

    因为使用 Python3.10 编译的,所以使用 Python3.10 测试。在路径PS E:\projects\ctpapi2python-swig-VisualStudio\vs2022> 下执行以下命令, userid/password 是在 simnow 平台注册的账号、密码

    PowerShell 复制代码
    > python demo_td.py tcp://180.168.146.187:10130 <userid> <password>
    CTP交易API版本号: v6.7.0_20230209  9:52:16.3535
    Connect to 180.168.146.187:10130
    Session -312410111 Connected
    交易前置连接成功
    > 认证请求
    认证响应 0 正确
    > 登录请求
    登录响应 0 正确
    ################# 按任意键退出
  2. 行情接口

    测试需要用到文件:

    text 复制代码
    demo_md.py
    iconv.dll
    charset.dll
    msvcp140.dll
    thostmduserapi_se.dll
    _thostmduserapi.pyd
    thosttmduserapi.py

    demo_md.py

    python 复制代码
    """
    行情API demo
    
    注意选择有效合约, 没有行情可能是过期合约或者不再交易时间内导致
    """
    
    import inspect
    import os
    import sys
    
    import thostmduserapi as mdapi
    
    # 前置地址 参考 http://121.37.80.177:50080/detail.html  SimNow
    FrontAddr = ""
    # 注意选择有效合约, 没有行情可能是过期合约或者不在交易时间内的原因
    # instruments = ["AP410"]
    
    
    class CMdSpiImpl(mdapi.CThostFtdcMdSpi):
        def __init__(self):
            super().__init__()
    
            flow_path = os.path.join(os.getcwd(), "flow_logs", 'market')
            if not os.path.exists(flow_path):
                os.makedirs(flow_path)
            flow_path = os.path.join(flow_path, 'market')
    
            self._api = mdapi.CThostFtdcMdApi.CreateFtdcMdApi(flow_path)
    
            print("CTP行情API版本号:", self._api.GetApiVersion())
    
            # 注册行情前置
            self._api.RegisterFront(FrontAddr)
            # 注册行情回调实例
            self._api.RegisterSpi(self)
            # 初始化行情实例
            self._api.Init()
            print("初始化成功")
    
        def OnFrontConnected(self):
            """行情前置连接成功"""
            print("行情前置连接成功")
    
            # 登录请求, 行情登录不进行信息校验
            print("登录请求")
            req = mdapi.CThostFtdcReqUserLoginField()
            self._api.ReqUserLogin(req, 0)
    
        def OnRspUserLogin(
                self,
                pRspUserLogin: mdapi.CThostFtdcRspUserLoginField,
                pRspInfo: mdapi.CThostFtdcRspInfoField,
                nRequestID: int,
                bIsLast: bool,
        ):
            """登录响应"""
            if pRspInfo and pRspInfo.ErrorID != 0:
                print(
                    f"登录失败: ErrorID={pRspInfo.ErrorID}, ErrorMsg={pRspInfo.ErrorMsg}"
                )
                return
    
            print("登录成功")
    
            if len(instruments) == 0:
                return
    
            # 订阅行情
            print("订阅行情请求:", instruments)
            self._api.SubscribeMarketData(
                [i.encode("utf-8") for i in instruments], len(instruments)
            )
    
        def OnRtnDepthMarketData(
                self, pDepthMarketData: mdapi.CThostFtdcDepthMarketDataField
        ):
            """深度行情通知"""
            params = []
            for name, value in inspect.getmembers(pDepthMarketData):
                if name[0].isupper():
                    params.append(f"{name}={value}")
            print("深度行情通知:", ",".join(params))
    
        def OnRspSubMarketData(
                self,
                pSpecificInstrument: mdapi.CThostFtdcSpecificInstrumentField,
                pRspInfo: mdapi.CThostFtdcRspInfoField,
                nRequestID: int,
                bIsLast: bool,
        ):
            """订阅行情响应"""
            if pRspInfo:
                print("订阅行情", pRspInfo.ErrorID, pRspInfo.ErrorMsg)
                return
    
    
    if __name__ == "__main__":
        if len(sys.argv) < 3:
            print("Usage:    python demo_td <frontaddr> <instrument_id1> <instrument_id2> ...")
            exit(-1)
    
        FrontAddr = sys.argv[1]
        # 注意选择有效合约, 没有行情可能是过期合约或者不在交易时间内的原因
        instruments = sys.argv[2:]
        spi = CMdSpiImpl()
        input("############# 按任意键退出 \n")
     

    执行如下命令, 命令后可跟多个有效合约:

    PowerShell 复制代码
    > python .\demo_md.py tcp://180.168.146.187:10131 AP401
     登录请求
     登录成功
     订阅行情请求: ['AP401']
     订阅行情 0 CTP:No Error
     深度行情通知: ActionDay=20240104,AskPrice1=8650.0,AskPrice2=1.7976931348623157e+308,AskPrice3=1.7976931348623157e+308,AskPrice4=1.7976931348623157e+308,AskPrice5=1.7976931348623157e+308,AskVolume1=1,AskVolume2=0,AskVolume3=0,AskVolume4=0,AskVolume5=0,AveragePrice=8600.0,BandingLowerPrice=0.0,BandingUpperPrice=0.0,BidPrice1=8600.0,BidPrice2=1.7976931348623157e+308,BidPrice3=1.7976931348623157e+308,BidPrice4=1.7976931348623157e+308,BidPrice5=1.7976931348623157e+308,BidVolume1=7,BidVolume2=0,BidVolume3=0,BidVolume4=0,BidVolume5=0,ClosePrice=1.7976931348623157e+308,CurrDelta=1.7976931348623157e+308,ExchangeID=,ExchangeInstID=,HighestPrice=8601.0,InstrumentID=AP401,LastPrice=8600.0,LowerLimitPrice=7745.0,LowestPrice=8600.0,OpenInterest=229.0,OpenPrice=8600.0,PreClosePrice=8557.0,PreDelta=0.0,PreOpenInterest=234.0,PreSettlementPrice=8606.0,SettlementPrice=1.7976931348623157e+308,TradingDay=20240105,Turnover=232200.0,UpdateMillisec=0,UpdateTime=21:39:43,UpperLimitPrice=9467.0,Volume=27 

以上就是使用 VisualStudio 2022 IDE 通过 swig 将 CTPAPI,装换为 python 的整个流程了。如果是使用其他版本的 VisualStudio 或 构建目标为 win32 平台,逻辑是一样的,只要参照以上,同时将相应库改为win32的即可。

同时,以上工程已上传至 openctp/ctpapi2python-swig-compile, 可下载参考。

Linux 平台转换

参考环境

  • Debian 12.4
  • g++ 12.2.0
  • swig 4.1.0
  • ctpapi 6.7.2

下载解压

下载地址 http://121.37.80.177:50080/Download/CTPAPI/CTP/ctp_6.7.2.zip

解压后得到 linux64 的头文件、库文件,如下:

bash 复制代码
$ ls
thostmduserapi_se.so  ThostFtdcMdApi.h      ThostFtdcUserApiDataType.h  
thosttraderapi_se.so  ThostFtdcTraderApi.h  ThostFtdcUserApiStruct.h    

将 so 文件重命名为 libthostmduserapi_se.so / libthosttraderapi_se.so

添加 .i文件

添加 thostmduserapi.i / thosttraderapi.i 文件:
thosttraderapi.i

text 复制代码
%module(directors="1") thosttraderapi
%{
#include "ThostFtdcTraderApi.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 %}
%typemap(out) char[ANY], char[] {
    const std::string &gbk($1);
    std::vector<wchar_t> wstr(gbk.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, gbk.data(), gbk.data() + gbk.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());
    }
}
%feature("python:annotations", "c");
%feature("director") CThostFtdcTraderSpi;
%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;

%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruCT.H"
%INCLUDE "THOSTFTDCTRADERAPI.H"

thostmduserapi.i

text 复制代码
%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("python:annotations", "c");
%feature("director") CThostFtdcMdSpi;

%typemap(out) char[ANY], char[] {
    const std::string &gbk($1);
    std::vector<wchar_t> wstr(gbk.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,
            gbk.data(), gbk.data() + gbk.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);
}

%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;

%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcMdApi.h"

文件如下:

bash 复制代码
$ ls
libthostmduserapi_se.so  ThostFtdcMdApi.h      ThostFtdcUserApiDataType.h  thostmduserapi.i
libthosttraderapi_se.so  ThostFtdcTraderApi.h  ThostFtdcUserApiStruct.h    thosttraderapi.i

swig根据 .i 文件生成 python接口文件和wrap文件

bash 复制代码
swig -threads -c++ -python thostmduserapi.i
swig -threads -c++ -python thosttraderapi.i

生成如下文件:

text 复制代码
thosttraderapi.py
thostmduserapi.py
thosttraderapi_wrap.h
thosttraderapi_wrap.cxx
thostmduserapi_wrap.h
thostmduserapi_wrap.cxx

构建编译

先创建 makefile 文件
makefile_traderapi

text 复制代码
OBJS=thosttraderapi_wrap.o
INCLUDE=-I./ -I/root/.pyenv/versions/3.7.17/include/python3.7m/
TARGET=_thosttraderapi.so
CPPFLAG=-shared -fPIC
CC=g++
LDLIB=-L. -lthosttraderapi_se
$(TARGET) : $(OBJS)
        $(CC) $(CPPFLAG) -Wl,-enable-new-dtags,-rpath,./ $(INCLUDE) -o $(TARGET) $(OBJS) $(LDLIB)
$(OBJS) : %.o : %.cxx
        $(CC) -c -fPIC $(INCLUDE) $< -o $@
clean:
        rm -f $(OBJS)
        -rm -f $(TARGET)

makefile_mduserapi

text 复制代码
OBJS=thostmduserapi_wrap.o
INCLUDE=-I./ -I/root/.pyenv/versions/3.7.17/include/python3.7m/
TARGET=_thostmduserapi.so
CPPFLAG=-shared -fPIC
CC=g++
LDLIB=-L. -lthostmduserapi_se
$(TARGET) : $(OBJS)
        $(CC) $(CPPFLAG) -Wl,-enable-new-dtags,-rpath,./ $(INCLUDE) -o $(TARGET) $(OBJS) $(LDLIB)
$(OBJS) : %.o : %.cxx
        $(CC) -c -fPIC $(INCLUDE) $< -o $@
clean:
        -rm -f $(OBJS)
        -rm -f $(TARGET)

注意makefile文件中的python包含路径/root/.pyenv/versions/3.7.17/include/python3.7m/要换成自己的

执行编译命令:

bash 复制代码
make --file=makefile_traderapi
make --file=makefile_mduserapi

会得到两个动态库: _thosttraderapi.so / _thostmduserapi.so

测试

参考windows中的测试文件和命令

相关文件已上传至 openctp/ctpapi2python-swig-compile,

MacOS 平台转换

待续...

---

作者: Jedore

出处: jedore.top/blog/post/c...

版权: CC BY-NC-ND 4.0

声明: 本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接.

相关推荐
databook1 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar2 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户8356290780512 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_2 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机9 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机10 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机10 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机10 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i10 小时前
drf初步梳理
python·django
每日AI新事件10 小时前
python的异步函数
python