ctp-rust 1.0.1

Safe Rust bindings for CTP (Comprehensive Transaction Platform) and its variations for Chinese financial markets
Documentation

CTP Rust SDK

一个为CTP (综合交易平台) 提供的安全、现代化的Rust绑定库,支持Linux和macOS系统。

✨ 特性

  • 🔒 类型安全: 使用Rust的类型系统确保内存安全和线程安全
  • 🌍 跨平台支持: 原生支持Linux和macOS (x86_64/ARM64)
  • 📝 编码自动转换: 自动处理GB18030到UTF-8的编码转换
  • 高性能: 零拷贝设计,最小化性能开销
  • 🛡️ 安全FFI: 安全的C++库绑定,避免内存泄露和悬空指针
  • 🐛 调试支持: 内置Debug日志功能,可配置日志输出

🚀 快速开始

项目结构

直接克隆并使用本项目:

git clone <repository-url>
cd ctp-rust

基本使用示例

整体架构流程

Rust Application ↓ 调用API Rust FFI (ffi.rs) ↓ 调用C函数 C++ Wrapper (ctp_wrapper.cpp) ↓ 调用C++类方法 CTP C++ API (libthostmduserapi_se.dylib) ↓ 回调事件 C++ Bridge (spi_bridge.cpp) ↓ 转发回调 Rust Callback Functions ↓ 处理业务逻辑 Rust Application

行情订阅

use ctp_rust::*;
use ctp_rust::api::{MdApi, CtpApi};
use ctp_rust::api::md_api::{MdSpiHandler, DepthMarketDataField};
use ctp_rust::types::{ReqUserLoginField, RspUserLoginField, RspInfoField};

// 实现行情回调处理器
struct MyMdHandler;

impl MdSpiHandler for MyMdHandler {
    fn on_front_connected(&mut self) {
        println!("行情前置已连接");
    }
    
    fn on_rsp_user_login(
        &mut self,
        user_login: Option<RspUserLoginField>,
        rsp_info: Option<RspInfoField>,
        request_id: i32,
        is_last: bool,
    ) {
        if let Some(rsp) = rsp_info {
            if rsp.is_success() {
                println!("登录成功");
            } else {
                if let Ok(msg) = rsp.get_error_msg() {
                    println!("登录失败: {}", msg);
                }
            }
        }
    }
    
    fn on_rtn_depth_market_data(&mut self, market_data: DepthMarketDataField) {
        println!("收到行情数据,最新价: {}", market_data.last_price);
    }
}

#[tokio::main]
async fn main() -> CtpResult<()> {
    // 创建行情API
    let mut md_api = MdApi::new(Some("./flow"), false, false)?;
    
    // 注册回调处理器
    md_api.register_spi(MyMdHandler)?;
    
    // 注册前置机地址
    md_api.register_front("tcp://180.168.146.187:10131")?;
    
    // 初始化
    md_api.init()?;
    
    // 创建登录请求
    let login_req = ReqUserLoginField::new("9999", "投资者账号", "密码")?
        .with_product_info("MyApp")?;
    
    // 发送登录请求
    md_api.req_user_login(&login_req)?;
    
    // 订阅行情
    md_api.subscribe_market_data(&["rb2501", "i2501"])?;
    
    // 等待API退出
    md_api.join()?;
    
    Ok(())
}

交易接口(同步)

use ctp_rust::api::{TraderApi, CtpApi};
use ctp_rust::api::trader_api::{TraderSpiHandler, ReqAuthenticateField};

struct MyTraderHandler;

impl TraderSpiHandler for MyTraderHandler {
    fn on_front_connected(&mut self) {
        println!("交易前置已连接");
    }
    
    fn on_rsp_authenticate(
        &mut self,
        rsp_authenticate: Option<ctp_rust::api::trader_api::RspAuthenticateField>,
        rsp_info: Option<RspInfoField>,
        request_id: i32,
        is_last: bool,
    ) {
        if let Some(rsp) = rsp_info {
            if rsp.is_success() {
                println!("认证成功");
            }
        }
    }
}

#[tokio::main]
async fn main() -> CtpResult<()> {
    // 创建交易API
    let mut trader_api = TraderApi::new(Some("./flow"))?;
    
    // 注册回调处理器
    trader_api.register_spi(MyTraderHandler)?;
    
    // 注册前置机地址
    trader_api.register_front("tcp://180.168.146.187:10130")?;
    
    // 初始化
    trader_api.init()?;
    
    // 客户端认证
    let auth_req = ReqAuthenticateField {
        broker_id: [0; 11], // 填入实际的经纪公司代码
        user_id: [0; 16],   // 填入实际的用户代码
        // ... 其他字段
        ..Default::default()
    };
    
    trader_api.req_authenticate(&auth_req)?;
    
    Ok(())
}

异步交易接口

use ctp_rust::api::AsyncTraderApi;
use ctp_rust::api::async_trader_api::AsyncTraderEvent;
use ctp_rust::types::{QryTradingAccountField, ReqUserLoginField};

#[tokio::main]
async fn main() -> CtpResult<()> {
    // 创建异步交易API
    let async_trader = AsyncTraderApi::new(Some("./flow"), Some(true)).await?;
    
    // 注册前置机并初始化
    async_trader.register_front("tcp://180.168.146.187:10130").await?;
    async_trader.init().await?;
    
    // 等待连接
    async_trader.wait_connected(30).await?;
    
    // 异步登录
    let login_req = ReqUserLoginField::new("9999", "投资者账号", "密码")?;
    let login_info = async_trader.login(&login_req, 30).await?;
    println!("登录成功: {:?}", login_info);
    
    // 异步查询资金账户
    let account_query = QryTradingAccountField::new("9999", "投资者账号")?;
    let accounts = async_trader.qry_trading_account(&account_query, 10).await?;
    
    for account in accounts {
        println!("可用资金: {:.2}", account.available);
        println!("当前余额: {:.2}", account.balance);
    }
    
    // 监听事件
    while let Some(event) = async_trader.recv_event().await {
        match event {
            AsyncTraderEvent::OrderReturn(order) => {
                println!("收到报单回报: {:?}", order);
            }
            AsyncTraderEvent::TradeReturn(trade) => {
                println!("收到成交回报: {:?}", trade);
            }
            _ => {}
        }
    }
    
    Ok(())
}

📖 详细文档

系统要求

  • 操作系统: Linux (x86_64) 或 macOS (x86_64/ARM64)
  • Rust版本: 1.70+
  • CTP库: 需要相应平台的CTP动态库

目录结构

ctp-rust/
├── src/
│   ├── lib.rs              # 主库入口
│   ├── error.rs            # 错误定义
│   ├── encoding.rs         # 编码转换
│   ├── types.rs            # 数据类型定义
│   ├── ffi.rs              # FFI绑定声明
│   └── api/
│       ├── mod.rs          # API模块
│       ├── md_api.rs       # 行情API
│       └── trader_api.rs   # 交易API
├── libs/ctp/
│   ├── common/
│   │   ├── debug_logger.h  # 调试日志头文件
│   │   └── debug_logger.cpp # 调试日志实现
│   ├── linux/
│   │   ├── include/        # Linux CTP头文件
│   │   ├── lib/            # Linux动态库
│   │   └── wrapper/        # Linux C++包装器
│   └── mac64/
│       ├── include/        # macOS CTP头文件
│       ├── thostmduserapi_se.framework/ # macOS框架
│       ├── thosttraderapi_se.framework/
│       └── wrapper/        # macOS C++包装器
├── examples/               # 示例代码
└── build.rs                # 构建脚本

编码处理

CTP库使用GB18030编码,而Rust默认使用UTF-8。本SDK自动处理两种编码之间的转换:

use ctp_rust::encoding::GbkConverter;

// UTF-8 转 GB18030
let gb_bytes = GbkConverter::utf8_to_gb18030("期货合约")?;

// GB18030 转 UTF-8
let utf8_string = GbkConverter::gb18030_to_utf8(&gb_bytes)?;

// 固定长度字节数组转换
use ctp_rust::types::{InstrumentIdType, StringConvert};

let instrument_id = InstrumentIdType::from_utf8_string("rb2501")?;
let back_to_string = instrument_id.to_utf8_string()?;

错误处理

SDK提供了完整的错误处理机制:

use ctp_rust::error::{CtpError, CtpResult};

match some_ctp_function() {
    Ok(result) => println!("操作成功: {:?}", result),
    Err(CtpError::ConnectionError(msg)) => println!("连接错误: {}", msg),
    Err(CtpError::AuthenticationError(msg)) => println!("认证错误: {}", msg),
    Err(CtpError::BusinessError(code, msg)) => {
        println!("业务错误 [{}]: {}", code, msg);
    }
    Err(e) => println!("其他错误: {}", e),
}

平台支持

Linux

# 确保系统有必要的库
sudo apt-get install build-essential

# 设置库文件路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./libs/ctp/linux/lib

# 运行程序
cargo run

macOS

# 设置库文件路径
export DYLD_FRAMEWORK_PATH=$DYLD_FRAMEWORK_PATH:./libs/ctp/mac64
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:./libs/ctp/mac64/wrapper

# 在Apple Silicon Mac上可能需要Rosetta
# 如果遇到架构问题,可以用Rosetta运行:
arch -x86_64 cargo run

🔧 构建配置

构建和调试日志

项目内置了调试日志功能,可以帮助排查问题:

use ctp_rust::ffi::{CTP_InitializeDebugLogging, CtpLogConfig};
use std::ffi::CString;

// 启用控制台日志输出
let log_path = CString::new("").unwrap();
let config = CtpLogConfig {
    enable_debug: 1,
    log_file_path: std::ptr::null(), // 使用null输出到控制台
    max_file_size_mb: 10,
    max_backup_files: 5,
};

unsafe {
    CTP_InitializeDebugLogging(&config);
}

// 或者输出到文件
let log_path = CString::new("./ctp_debug.log").unwrap();
let config = CtpLogConfig {
    enable_debug: 1,
    log_file_path: log_path.as_ptr(),
    max_file_size_mb: 10,
    max_backup_files: 5,
};

编译和运行

# 构建项目(会自动编译C++包装器)
cargo build

# 运行示例
cargo run --example trader_basic

自定义构建

如果你需要自定义CTP库的位置,可以设置环境变量:

export CTP_LIB_PATH=/path/to/your/ctp/libs
export CTP_INCLUDE_PATH=/path/to/your/ctp/headers

功能特性

项目使用条件编译,根据可用的CTP库自动启用相应功能:

  • md_api: 当找到行情API库时自动启用
  • trader_api: 当找到交易API库时自动启用
  • test_mode: 当没有CTP库时启用测试模式

📋 API参考

主要类型

  • MdApi: 同步行情API接口
  • AsyncMdApi: 异步行情API接口
  • TraderApi: 同步交易API接口
  • AsyncTraderApi: 异步交易API接口
  • ReqUserLoginField: 用户登录请求
  • RspUserLoginField: 用户登录响应
  • DepthMarketDataField: 深度行情数据
  • OrderField: 报单信息
  • TradeField: 成交信息

回调接口

  • MdSpiHandler: 行情回调处理接口(同步)
  • TraderSpiHandler: 交易回调处理接口(同步)
  • AsyncMdEvent: 异步行情事件
  • AsyncTraderEvent: 异步交易事件

编码工具

  • GbkConverter: GB18030/UTF-8编码转换器
  • StringConvert: 字符串转换特质

🧪 测试

运行所有测试:

cargo test

运行编码测试:

cargo test --test encoding_tests

运行集成测试(需要CTP库):

cargo test --features integration

📚 示例

更多示例请查看 examples/ 目录:

  • md_basic.rs: 基础行情订阅(同步)
  • async_md_basic.rs: 基础行情订阅(异步)
  • trader_basic.rs: 基础交易功能,包含资金账户和持仓查询(同步)
  • async_trader_basic.rs: 基础交易功能(异步)
  • encoding_demo.rs: 编码转换示例
  • error_handling.rs: 错误处理示例
  • debug_logger.rs: 调试日志功能示例

运行示例

# 设置环境变量(以实际配置为准)
export CTP_BROKER_ID="9999"
export CTP_INVESTOR_ID="your_investor_id"
export CTP_PASSWORD="your_password"
export CTP_MD_FRONT="tcp://180.168.146.187:10131"
export CTP_TRADER_FRONT="tcp://180.168.146.187:10130"
export CTP_FLOW_PATH="./ctp_flow"

# 运行交易示例(macOS)
DYLD_FRAMEWORK_PATH=libs/ctp/mac64 DYLD_LIBRARY_PATH=libs/ctp/mac64/wrapper cargo run --example trader_basic

# 运行交易示例(Linux)
LD_LIBRARY_PATH=libs/ctp/linux/lib:libs/ctp/linux/wrapper cargo run --example trader_basic

🛠️ 开发指南

项目架构

Rust应用程序
    ↓ 调用API
Rust FFI层 (ffi.rs)
    ↓ 调用C函数
C++ Wrapper层 (ctp_wrapper.cpp)
    ↓ 调用C++方法
CTP C++ API (Framework/动态库)
    ↓ SPI回调
C++ Bridge层 (spi_bridge.cpp)
    ↓ 转发回调
Rust回调函数
    ↓ 业务逻辑处理
Rust应用程序

架构特点:

  • 跨平台兼容: Linux使用.so动态库,macOS使用.framework
  • 调试日志: 内置debug_logger提供详细的调用跟踪
  • 内存安全: 使用RAII和智能指针管理资源

添加新的 API 方法

  1. 更新 C++ wrapper

    // 在 ctp_wrapper.h 中添加函数声明
    int CThostFtdcTraderApi_ReqNewFunction(void* api, void* req, int request_id);
    
    // 在 ctp_wrapper.cpp 中添加实现
    int CThostFtdcTraderApi_ReqNewFunction(void* api, void* req, int request_id) {
        if (api) {
            return static_cast<CThostFtdcTraderApi*>(api)->ReqNewFunction(
                static_cast<RequestStruct*>(req), request_id);
        }
        return -1;
    }
    
  2. 更新 Rust FFI 声明

    // 在 src/ffi.rs 中添加
    extern "C" {
        pub fn CThostFtdcTraderApi_ReqNewFunction(
            api: *mut c_void,
            req: *const c_void,
            request_id: c_int,
        ) -> c_int;
    }
    
  3. 更新 Rust API

    // 在 src/api/trader_api.rs 中添加
    pub fn req_new_function(&mut self, req: &RequestStruct) -> CtpResult<i32> {
        // 实现逻辑
    }
    
  4. 重新编译项目

    cargo build
    

添加新的回调

  1. 更新 SPI Bridge

    // 在 spi_bridge.h 中添加回调类型
    typedef void (*OnNewCallbackFunction)(void* user_data, void* data, int param);
    
    // 在 TraderSpiCallbacks 结构中添加
    OnNewCallbackFunction on_new_callback;
    
    // 在 spi_bridge.cpp 中实现
    virtual void OnNewCallback(DataStruct* data, int param) override {
        if (callbacks.on_new_callback) {
            callbacks.on_new_callback(callbacks.user_data, data, param);
        }
    }
    
  2. 更新 Rust 回调处理

    // 在 TraderSpiHandler trait 中添加
    fn on_new_callback(&mut self, data: DataStruct, param: i32) {
        // 默认实现
    }
    
    // 实现回调函数
    extern "C" fn on_new_callback_impl(
        user_data: *mut c_void,
        data: *mut c_void,
        param: c_int,
    ) {
        // 回调实现
    }
    

调试技巧

  1. 启用调试日志

    env_logger::init();
    log::debug!("调试信息");
    
  2. 使用 gdb/lldb 调试 C++ 部分

    # macOS
    lldb target/debug/examples/trader_basic
    
    # Linux
    gdb target/debug/examples/trader_basic
    
  3. 内存安全检查

    cargo valgrind run --example trader_basic
    

🤝 贡献

欢迎贡献代码!请遵循以下步骤:

  1. Fork本仓库
  2. 创建feature分支 (git checkout -b feature/amazing-feature)
  3. 提交你的修改 (git commit -m 'Add some amazing feature')
  4. 推送到分支 (git push origin feature/amazing-feature)
  5. 开启Pull Request

⚠️ 注意事项

  1. 库文件: 需要获取相应平台的CTP库文件并放置在正确位置
  2. 网络环境: 连接CTP服务器需要稳定的网络环境
  3. 账户权限: 需要有效的CTP账户和相应的交易权限
  4. 风险提示: 期货交易有风险,请谨慎使用

📄 许可证

本项目采用MIT或Apache-2.0双重许可证。详见 LICENSE-MITLICENSE-APACHE 文件。

🔗 相关链接

📞 联系方式

如有问题或建议,请通过以下方式联系:

  • GitHub Issues: 通过项目仓库提交问题

免责声明: 本软件仅供学习和研究使用,作者不对因使用本软件导致的任何损失承担责任。期货投资有风险,入市需谨慎。