封装CTP API为Java SDK
前言
新接触的一个项目,需要获取期货的实时报价信息。结合数据来源的权威性和时效性,决定采用CTP API。CTP API提供的是C++库,并给出了Python实现,因为项目是使用Java写的,所以需要自己来编译C++库完成Java实现。
一、准备工作
需要安装CTP API SDK、JDK、SWIG和Visual Studio4个工具。
1. 下载CTP API SDK
访问http://www.openctp.cn/CTPAPI.html,网站提供了CTP API(期货合约)和CTP股票期权API(股票期权合约)两种接口库,按需选择对应库文件下载,这里下载的是CTP API ctpapi-6.7.11.zip。
解压ctpapi-6.7.11.zip文件,包含以下内容:
其中v6.7.11_xxx_linux64是linux环境相关文件,v6.7.11_xxx_winApi是windows环境相关文件,因为是在Win11环境进行编译和开发,所以选择winApi文件夹下的文件。要注意的是,windows环境的文件区分32位和64位,目前大多数windows操作系统(win10、win11)都是64位的,所以要选择64位文件,如下图所示,文件名带“64”的是64位文件目录。
最终我们需要的文件包括:
ThostFtdcMdApi.h:行情API头文件
ThostFtdcTraderApi.h:交易API头文件
ThostFtdcUserApiDataType.h:数据类型定义头文件
ThostFtdcUserApiStruct.h:数据接口定义头文件
thostmduserapi_se.dll:行情动态链接库
thostmduserapi_se.lib:行情导入库
thosttraderapi_se.dll:交易动态链接库
thosttraderapi_se.lib:交易导入库
2. 下载JDK
访问https://www.oracle.com/cn/java/technologies/downloads/下载JDK,根据自己的需求选择对应版本,尽量选择JDK8+。安装步骤参考网上其他教程。我安装的目录是D:\Program Files\Java\jdk-25,记住这个路径后面会用到。
3. 下载编译工具SWIG
访问https://sourceforge.net/projects/swig/files/,下载swigwin,这里以swigwin-4.4.0为例,下载zip包后解压到指定目录,例如D:\develop\swigwin-4.4.0,然后配置环境变量(Path)。
打开终端,输入swig -version,看到版本信息说明安装配置成功。
4. 安装Visual Studio
访问https://visualstudio.microsoft.com/zh-hans/downloads/,下载Visual Studio,我下载的是Visual Studio 2026 Community。注意,选择Visual Studio,而不是Visual Studio Code!!!安装时勾选 “桌面开发用 C++”(确保包含 MSVC 编译器、Windows SDK),后续安装和配置步骤可参考网上相关教程。
二、编译步骤
第一步:文件目录结构搭建
新建一个文件夹并重新命名,目录和文件夹名称尽量不要有空格和中文,比如我创建的文件夹名是CTP-Java-SDK,路径是D:\develop\CTP-Java-SDK。在该目录下再新建ctb_lib目录,将ctpapi-6.7.11.zip中解压出来的8个头文件和库文件全部复制到此目录中;在ctb_lib同级目录下新建swig目录,从swig的安装目录swigwin-4.4.0\Lib\java\下复制various.i文件到此目录中,再手动创建一个ctp.i的空文件(swig接口文件,后面会用到);在ctb_lib同级目录下新建src\com\ctp\sdk(存放java文件)、wrap(存放C++包装类文件)、vs_project(vs2026 dll项目目录)和final(存放最终可用的文件)几个空文件夹。最后的文件目录结构如下:
D:\develop\CTP-Java-SDK ├─ ctp_lib # CTP原始文件 │ ├─ ThostFtdcMdApi.h │ ├─ ThostFtdcTraderApi.h │ ├─ ThostFtdcUserApiDataType.h │ ├─ ThostFtdcUserApiStruct.h │ ├─ thostmduserapi_se.lib │ ├─ thosttraderapi_se.lib │ ├─ thostmduserapi_se.dll │ └─ thosttraderapi_se.dll ├─ swig # SWIG相关文件 │ ├─ ctp.i # SWIG接口文件 │ └─ various.i # 从SWIG安装目录复制(D:\swigwin-4.4.0\Lib\java\various.i) ├─ src # 生成的Java类(自动生成) │ └─ com │ └─ stp │ └─ sdk ├─ wrap # 生成的C++包装代码(自动生成) ├─ vs_project # VS2026 DLL项目(后续创建) └─ final # 最终生成的文件 第二步:编写 SWIG 接口文件(ctp.i)
使用notepad++或系统自带的文本编辑器打开ctp.i文件,填写下面的内容,如果上面的文件目录和我一样,可直接复制这段C++代码,也可以自行修改:
%module(directors="1") CTPJava // 生成的Java类前缀(最终Java类名:CTPJavaXXX),可自定义 %include "various.i" // 处理数组参数(订阅合约用) // 映射char**为Java字符串数组(适配CTP订阅合约接口) %apply char **STRING_ARRAY { char *ppInstrumentID[] } %{ // 1. 引入CTP头文件,注意改为实际路径 #include "../ctp_lib/ThostFtdcMdApi.h" #include "../ctp_lib/ThostFtdcTraderApi.h" #include "../ctp_lib/ThostFtdcUserApiDataType.h" #include "../ctp_lib/ThostFtdcUserApiStruct.h" // 2. 中文乱码转换(使用Windows API,兼容VS2026) #include <windows.h> // 引入Windows API头文件 #include <string> using namespace std; // GB2312转UTF-8(Windows API实现,兼容所有VS版本) string GB2312ToUTF8(const char* gb2312_str) { if (gb2312_str == nullptr || *gb2312_str == '\0') { return ""; } // 第一步:GB2312转宽字符(WCHAR) int wchar_len = MultiByteToWideChar( CP_ACP, // GB2312编码(CP_ACP对应系统默认ANSI编码,Windows下即GB2312) 0, // 转换选项 gb2312_str, // 输入GB2312字符串 -1, // 自动计算长度(包含结束符) nullptr, // 输出缓冲区(先获取长度) 0 // 输出缓冲区大小(0表示仅获取长度) ); WCHAR* wchar_buf = new WCHAR[wchar_len]; MultiByteToWideChar(CP_ACP, 0, gb2312_str, -1, wchar_buf, wchar_len); // 第二步:宽字符转UTF-8 int utf8_len = WideCharToMultiByte( CP_UTF8, // 目标编码UTF-8 0, // 转换选项 wchar_buf, // 输入宽字符 -1, // 自动计算长度 nullptr, // 输出缓冲区(先获取长度) 0, // 输出缓冲区大小(0表示仅获取长度) nullptr, // 无效字符替换(默认) nullptr // 是否使用替换字符(默认) ); char* utf8_buf = new char[utf8_len]; WideCharToMultiByte(CP_UTF8, 0, wchar_buf, -1, utf8_buf, utf8_len, nullptr, nullptr); // 生成结果并释放内存 string utf8_str(utf8_buf); delete[] wchar_buf; delete[] utf8_buf; return utf8_str; } %} // 中文编码GB2312ToUTF8 %typemap(out) char[], char[ANY] { string utf8_str = GB2312ToUTF8($1); $result = JCALL1(NewStringUTF, jenv, utf8_str.c_str()); } // 允许Java继承CTP的Spi回调类,并让 C++ 层能反向调用 Java 重写的方法(实现回调逻辑) %feature("director") CThostFtdcMdSpi; %feature("director") CThostFtdcTraderSpi; // 测试时发现没有自动触发OnRtnDepthMarketData()方法,所以这里显示声明一下,确保 SWIG 识别并生成转发代码) %extend CThostFtdcMdSpi { // 显式声明 OnRtnDepthMarketData 回调(与CTP头文件签名一致) virtual void OnRtnDepthMarketData(const CThostFtdcDepthMarketDataField* pDepthMarketData) {} } // 忽略CTP冗余常量(避免编译冲突) %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; // 引入CTP结构体和接口(顺序必须:先数据类型,后API) %include "../ctp_lib/ThostFtdcUserApiDataType.h" // 结构体类型 %include "../ctp_lib/ThostFtdcUserApiStruct.h" // 结构体定义 %include "../ctp_lib/ThostFtdcMdApi.h" // 行情接口(包含 MdSpi 所有回调) %include "../ctp_lib/ThostFtdcTraderApi.h" // 交易接口(包含 TraderSpi 所有回调) 第三步:用 SWIG 生成包装代码
打开命令提示符(管理员模式),进入D:\develop\CTP-Java-SDK\swig,执行以下命令:
swig -c++ -java -package com.ctp.sdk -outdir ../src/com/ctp/sdk -o ../wrap/ctp_wrap.cpp ctp.i - 参数说明:
-c++:处理 C++ 代码;-java:生成 Java 绑定;-package com.ctp.sdk:Java 类的包名(后续导入需用),可自定义;-outdir ../src/com/ctp/sdk:Java 类输出到src/com/ctp/sdk目录;-o ../wrap/ctp_wrap.cpp:C++ 包装代码输出到wrap目录;
如果没有报错(两个警告不用管),此时src/com/ctp/sdk目录中已生成java文件(我生成的是510个.java文件);D:\develop\CTP-Java-SDK\wrap目录下生成C++包装代码文件。
第四步:用 VS2026 编译 JNI 动态库
4.1 新建 VS DLL 项目
- 打开 VS2026,点击 “创建新项目”;
- 搜索 “动态链接库 (DLL)”,选择 “C++” 模板,命名为
CTPJavaDLL,保存到D:\develop\CTP_Java_SDK\vs_project;
勾选“将解决方案和项目放在同一目录中”,点击 “创建”。
4.2 添加文件到项目
在VS2026顶部工具栏中将Debug改为Release。
- 鼠标右键项目 → “添加” → “现有项”:
- 添加
D:\develop\CTP_Java_SDK\wrap\ctp_wrap.cpp(C++ 包装代码);
- 添加
添加 D:\develop\CTP_Java_SDK\ctp_lib 下的所有 .h 头文件。
添加成功后,项目头文件和源文件会显示添加的文件,如图:
4.3 配置项目属性(关键)
右键项目→属性
- 配置管理器</