rust:ctp的rust版本如何手工封装

author author     2023-04-29     213

关键词:

https://blog.csdn.net/wowotuo/article/details/86669758

这里指的手工封装,是指不用外部类似swig专用的库。

一、库、配置
1、DLL 交互的库
(1)libloading
https://github.com/nagisa/rust_libloading
(2)libc
Raw FFI bindings to platform libraries like libc.
https://github.com/rust-lang/libc

2、类型转换库
Rust封装CTP,涉及两个方面类型,一个是rust转c++; 另一个是c++转rust.

cbindgen: 可以根据此库,把rust的rs文件生成相应的头文件,提高效率,减少手工封装过程。
https://crates.io/crates/cbindgen

rust-bindgen: 可以根据此库将C/C++的头文件,自动生成Rust 的C绑定文件。
https://github.com/rust-lang/rust-bindgen

二、CTP DLL资料
http://www.sfit.com.cn/5_2_DocumentDown.htm

Api有3种通讯模式:
• 对话通讯模式:由客户端主动发起请求。Thost收到请求、处理请求后,返回1条或者多条响应纪录。例如登入、各项查询、报单、撤单等操作。
• 私有通讯模式:由Thost主动向客户端发出的相关信息。例如委托回报、成交回报、错单回报等
• 广播通讯模式:由Thost主动向所有客户端发出的公共信息,例如行情等。

文件名 文件描述
FtdcTraderApi.h 交易接口头文件
ThostFtdcMdApi.h 行情接口头文件
FtdcUserApiStruct.h 定义了一系列业务相关的数据结构头文件
FtdcUserApiDataType.h 定义了 API 所需的一系列数据类型头文件
thosttraderapi.dll,thostmduserapi.dll 动态链接库二进制文件
thostraderapi.lib,thostmduserapi.lib 导入库文件

CTP相关知识、封装具体的介绍可以参考:

https://zhuanlan.zhihu.com/p/20031646
Python量化交易平台开发教程系列1-类CTP交易API的工作原理
https://zhuanlan.zhihu.com/p/20031660
Python量化交易平台开发教程系列2-类CTP交易API的Python封装设计

关于测试环境SIMNOW:

CTP开发中使用的模拟账号密码,要到SIMNOW上注册。BrokerID为9999,账号即investorId,密码为SIMNOW的登陆密码。

三、CTP中 C++ .h文件的改写

在ctp文件中,有一类是数据结构.h;一类是类和虚方法接口的.h文件;量大,需要写一个自动转换的程序。

1、ThostFtdcUserApiDataType.h和ThostFtdcUserApiStruct.h

(1)ThostFtdcUserApiDataType.h头文件中的类型

比如:

/
///TFtdcTraderIDType是一个交易所交易员代码类型
/
typedef char TThostFtdcTraderIDType[21];

/
///TFtdcInvestorIDType是一个投资者代码类型
/
typedef char TThostFtdcInvestorIDType[13];

/
///TFtdcBrokerIDType是一个经纪公司代码类型
/
typedef char TThostFtdcBrokerIDType[11];

/
///TFtdcBrokerAbbrType是一个经纪公司简称类型
/
typedef char TThostFtdcBrokerAbbrType[9];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(2)ThostFtdcUserApiStruct.h中结构体

struct CThostFtdcDisseminationField

    TThostFtdcSequenceSeriesType    SequenceSeries;
    TThostFtdcSequenceNoType    SequenceNo;
;
1
2
3
4
5
要转换成rust 中的结构体:[名字和类型可以沿用原来的,只是换个方式],比如:

pub struct CThostFtdcDisseminationField

    pub SequenceSeries : i16 ,
    pub SequenceNo : i32 ,

pub struct CThostFtdcReqUserLoginField

    pub TradingDay : & \'static str ,
    pub BrokerID : & \'static str ,
    pub UserID : & \'static str ,
    pub Password : & \'static str ,
    pub UserProductInfo : & \'static str ,
    pub InterfaceProductInfo : & \'static str ,
    pub ProtocolInfo : & \'static str ,
    pub MacAddress : & \'static str ,
    pub OneTimePassword : & \'static str ,
    pub ClientIPAddress : & \'static str ,
    pub LoginRemark : & \'static str ,

pub struct CThostFtdcRspUserLoginField

    pub TradingDay : & \'static str ,
    pub LoginTime : & \'static str ,
    pub BrokerID : & \'static str ,
    pub UserID : & \'static str ,
    pub SystemName : & \'static str ,
    pub FrontID : i32 ,
    pub SessionID : i32 ,
    pub MaxOrderRef : & \'static str ,
    pub SHFETime : & \'static str ,
    pub DCETime : & \'static str ,
    pub CZCETime : & \'static str ,
    pub FFEXTime : & \'static str ,
    pub INETime : & \'static str ,


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
转换成rust中这个类型后,rust才能使用。
细节:这里暂时没有考虑int->c_int之类。只是提供一个方法。

2、tradeApi.h 和mdApi.h的改写

(1)主要是把类改成struct;

tradeapi中:

class CThostFtdcTraderSpi
class TRADER_API_EXPORT CThostFtdcTraderApi
1
2
mdapi中:

class CThostFtdcMdSpi
class MD_API_EXPORT CThostFtdcMdApi
1
2
(2)还有虚方法改成接口 trait;比如:

virtual void OnFrontConnected();

///当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
///@param nReason 错误原因
///        0x1001 网络读失败
///        0x1002 网络写失败
///        0x2001 接收心跳超时
///        0x2002 发送心跳失败
///        0x2003 收到错误报文
virtual void OnFrontDisconnected(int nReason);
1
2
3
4
5
6
7
8
9
10
四、封装:Rust与C++交互的核心技术

封装的目标是什么,简单地说,需要根据http://www.sfit.com.cn/5_2_DocumentDown.htm提供的4个头文件,两个C++原生的dll,以及lib文件,生成一个(或两个)封装的dll:
使之,

能够接受原生dll回调信息,与c++交互;
能够接受rust中参数,与原生dll交互;

(一)关于相关库
相关的库还是不少的,下面只是一些例子。

cc-rs:
https://github.com/alexcrichton/cc-rs
A library to compile C/C++/assembly into a Rust library/application.

libloading
https://github.com/nagisa/rust_libloading
A memory-safer wrapper around system dynamic library loading primitives. The most important safety guarantee by this library is prevention of dangling-Symbols that may occur after a Library is unloaded.

bindgen
bindgen automatically generates Rust FFI bindings to C (and some C++) libraries.
https://github.com/rust-lang/rust-bindgen

(二)关于封装

1、封装Rust 调用原生dll部分
比如Req开头的:

extern crate libloading as lib;
//const 
#[cfg(target_arch = "x86")]
fn load_ordinal_lib() -> Library
    Library::new("D:\\\\ctp-api\\\\tradeapi\\\\thostmduserapi.dll").expect("thostmduserapi.dll")

#[cfg(target_arch = "x86")]
//virtual void RegisterFront(char *pszFrontAddress) = 0;
fn RegisterFront(pszFrontAddress: String) -> lib::Result<()>
    let lib = load_ordinal_lib();
    unsafe
        let func: lib::Symbol<unsafe extern "C" fn(String) -> ()> = lib.get(b"RegisterFront")?;
        Ok(func(pszFrontAddress))
   

//virtual void RegisterNameServer(char *pszNsAddress) = 0;
//#[cfg(target_arch = "x86")]
fn RegisterNameServer(pszNsAddress: String) -> lib::Result<()>
    let lib = lib::Library::new("D:\\\\ctp-api\\\\tradeapi\\\\thostmduserapi.dll")?;
    unsafe
        let func: lib::Symbol<unsafe extern "C" fn(String) -> ()> =
            lib.get(b"RegisterNameServer")?;
        Ok(func(pszNsAddress))
   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2、封装原生dll回传给rust的部分
比如:On打开头的。
这个应是在C++中写的。方向与上面是相反的。

// Rust: 供c++中调用

pub extern "C" fn onRspUserLogin(
    pRspUserLogin: CThostFtdcRspUserLoginField,
    pRspInfo: CThostFtdcRspInfoField,
    nRequestID: i32,
    bIsLast: bool,
)

1
2
3
4
5
6
7
C++中:(大致的写法,相当于伪代码吧)

virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
      this->onRspUserLogin(pRspUserLogin, pRspInfo, nRequestID, bIsLast) //调用rust中的函数

1
2
3
4
这样,就把c++中的代码和rust函数之间的消息传递建立起来了。

下面文章值得参考一下:
https://blog.csdn.net/guiqulaxi920/article/details/78653054
Rust与C交互(FFI)中复杂类型的处理

五、与CTP 相关的一些具体技术问题

建议可以参考一下:
https://github.com/tashaxing/CTPtest

其中的难点在于:

int main()

    // 账号密码
    cout << "请输入账号: ";
    scanf("%s", gInvesterID);
    cout << "请输入密码: ";
    scanf("%s", gInvesterPassword);

    // 初始化行情线程
    cout << "初始化行情..." << endl;
    g_pMdUserApi = CThostFtdcMdApi::CreateFtdcMdApi();   // 创建行情实例
    CThostFtdcMdSpi *pMdUserSpi = new CustomMdSpi;       // 创建行情回调实例
    g_pMdUserApi->RegisterSpi(pMdUserSpi);               // 注册事件类
    g_pMdUserApi->RegisterFront(gMdFrontAddr);           // 设置行情前置地址
    g_pMdUserApi->Init();                                // 连接运行
    
    // 初始化交易线程
    cout << "初始化交易..." << endl;
    g_pTradeUserApi = CThostFtdcTraderApi::CreateFtdcTraderApi(); // 创建交易实例
    //CThostFtdcTraderSpi *pTradeSpi = new CustomTradeSpi;
    CustomTradeSpi *pTradeSpi = new CustomTradeSpi;               // 创建交易回调实例
    g_pTradeUserApi->RegisterSpi(pTradeSpi);                            // 注册事件类
    g_pTradeUserApi->SubscribePublicTopic(THOST_TERT_RESTART);    // 订阅公共流
    g_pTradeUserApi->SubscribePrivateTopic(THOST_TERT_RESTART);   // 订阅私有流
    g_pTradeUserApi->RegisterFront(gTradeFrontAddr);              // 设置交易前置地址
    g_pTradeUserApi->Init();                                      // 连接运行
        

    // 等到线程退出
    g_pMdUserApi->Join();
    delete pMdUserSpi;
    g_pMdUserApi->Release();

    g_pTradeUserApi->Join();
    delete pTradeSpi;
    g_pTradeUserApi->Release();

    // 转换本地k线数据
    //TickToKlineHelper tickToKlineHelper;
    //tickToKlineHelper.KLineFromLocalData("market_data.csv", "K_line_data.csv");
    
    getchar();
    return 0;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
其中,
(1) 初始化行情线程和初始化交易线程中难点;
(2) 这里c++的类在rust中只能用struct对应;
(3) 注册、回调和订阅在rust 中如何实现?

待续…
————————————————
版权声明:本文为CSDN博主「songroom」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wowotuo/article/details/86669758

我们如何才能完全删除 Ubuntu 安装的 Rust?

...2021-05-2718:55:33【问题描述】:我想将我的Rust更新到最新版本,但是当我搜索rustup时,它并没有出现在我的系统rustup:commandnotfound中。我尝试使用curl--proto\'=https\'--tlsv1.2-sSfhttps 查看详情

如何使用rust做静态编译,让编译出来的程序不再依赖其他库?

...干粮,不依赖这些七七八八的库呢?如果你用的是RUSTGNU版本,那可以直接发给客户,本身就自带了依赖项,不会有问题,本文主要说的是MSVC版本的RUST:找到~/.cargo/config文件,如果没有,请自己创建,然后加入以下代码,就启用... 查看详情

javascript如何封装插件

什么是封装呢?我的理解就是把一个功能单独做成一个组件,就像做饺子,以前做饺子必须自己先用面粉做饺子皮,再做饺子馅,然后再手工包饺子,但是现在人们发明了自动包饺子机器,虽然机器里面的每一步骤和你自己包饺... 查看详情

如何在调试 MSVC ABI Rust 程序时检查变量值?

...016-08-0521:22:51【问题描述】:我已经下载了Rust1.7.0的MSVCABI版本,并按照文档中的猜谜游戏部分进行操作。我注意到构建使用本机工具链(在我的例子中是VisualStudio2015Update2),因此生 查看详情

Rocket 每晚需要最低版本的 Rust,但已经安装了更高的稳定版本

】Rocket每晚需要最低版本的Rust,但已经安装了更高的稳定版本【英文标题】:RocketrequiresaminimumversionofRustnightly,butahigherstableversionisalreadyinstalled【发布时间】:2019-08-0909:12:57【问题描述】:我正在尝试运行Rocket,但我遇到了第一... 查看详情

rust编程语言入门之rust的面向对象编程特性(代码片段)

...,包括面向对象面向对象通常包含以下特性:命名对象、封装、继承对象包含数据和行为“设计模式四人帮”在《设计模型》中给面向对象的定义:面向对象的程序由对象组成对象包装了数据和操作这些数据的过程,这些过程通... 查看详情

如何手工卸载和安装ntkooffice文档控件

...般情况下应该让客户端自动控件,这样当服务器控件版本更新时,客户端可以获得自动升级方面的好处。但是,如果因为客户机配置有问题,或者有其它拦截工具拦截的原因无法自动安装控件,您可以采取本... 查看详情

rust学习

...问官网 https://www.rust-lang.org/tools/install 下载相应的版本进行安装。2. 如果是Windows版本的Rust,需要安装Visual Studio 2012以上版本的VC++ 才能编译通过3. 在命令行窗口中运行  rustc --version  &n... 查看详情

rust所有权语义模型

...a、Ruby、Golang、Elixir等语言都依赖于GC。而C/C++却是依赖于手工管理内存,程序员使用malloc和free函数来分配释放内存。GC技术经过这么多年的发展,是相对安全的内存管理,也解放了 查看详情

rust游戏怎么安装线路

参考技术A首先,通过rustup下载Rust,Rustup是用于管理所有Rust版本及其关联工具的命令行工具。在Windows上,打开链接https://www.rust-lang.org/install.html,然后按照说明安装Rust。按照所有说明进行操作之后,Rust将被安装并显示屏幕:安装... 查看详情

开票软件如何更新升级

...件,应该可以到当地税务局咨询 开票软件(金税盘版)版本升级方法  根据十三届全国人大第二次会议中政府报告中提出,实施更大规模的减税,惠普性减税与结构性减税并举,进行税率调整方案。进一步深化增值税改革,... 查看详情

vscode开发rust的配套插件

...。rust-analyzer是官方维护的rls(rust语言服务器)2.0版本BetterToml:使用toml做项目的配置管理。rustsyntax:为代码提供语法高亮。CodeLLDB:crates:帮助你分析当前项目的依赖是否是最新的版本。rusttestlens:可以帮你快... 查看详情

论如何快速整理文献

...的文献不应该被使用、整理和上传,请寻找对应的Publish版本)、supplementinformation、ppt、纯图片、中文文献等pdf,这些pdf文件需要手工处理有信息页的pdf可以使用工具剪切掉信息页。含有标记的pdf也需要手工寻找到对应版本的 查看详情

rust结构体数组怎么写?

...就好了,详细内容去读rfc文档。另外我们有很多手段可以手工drop,不使用默认drop顺序。这个顺序具体是什么只有极少数代码需要关注。 查看详情

rust闭包和haskelllambda有什么区别?[关闭](代码片段)

最近我asked关于如何将一堆具有相同签名的闭包放入Rust中的向量中。来自Haskell的背景,看起来有点复杂,Rust封装每个都有自己独特的类型,需要一个特征来代表它们。在Haskell中,lambda是通过其签名键入的,如果签名相同,则类... 查看详情

如何中止 Rust 进程?

】如何中止Rust进程?【英文标题】:HowdoIabortaRustprocess?【发布时间】:2016-12-0222:12:27【问题描述】:我正在包装一个C库,它执行一些可能会失败的mallocs。Rust的其余部分在OOM上中止,所以我也想做同样的事情。有std::intrinsics::abo... 查看详情

如何在生产环境排查rust内存占用过高问题(代码片段)

内存安全的Rust,虽然基本不会出现内存泄漏,但如何合理分配内存,是每个复杂应用都要面临的问题。往往随着业务的不同,相同的代码可能会产生不同的内存占用。因此,有不小的概率会出现内存使用过多... 查看详情

如何在生产环境排查rust内存占用过高问题(代码片段)

内存安全的Rust,虽然基本不会出现内存泄漏,但如何合理分配内存,是每个复杂应用都要面临的问题。往往随着业务的不同,相同的代码可能会产生不同的内存占用。因此,有不小的概率会出现内存使用过多... 查看详情