前言
新接触的一个项目,需要获取期货的实时报价信息。结合数据来源的权威性和时效性,决定采用 CTP API。CTP API 提供的是 C++ 库,并给出了 Python 实现,因为项目是使用 Java 写的,所以需要自己来编译 C++ 库完成 Java 实现。
一、准备工作
需要安装 CTP API SDK、JDK、SWIG 和 Visual Studio 4 个工具。
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,根据自己的需求选择对应版本,尽量选择 JDK 8+。安装步骤参考网上其他教程。我安装的目录是 D:\Program Files\Java\jdk-8,记住这个路径后面会用到。
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 2022 Community。注意,选择 Visual Studio,而不是 Visual Studio Code!!!安装时勾选 '桌面开发用 C++'(确保包含 MSVC 编译器、Windows SDK),后续安装和配置步骤可参考网上相关教程。
二、编译步骤
第一步:文件目录结构搭建
新建一个文件夹并重新命名,目录和文件夹名称尽量不要有空格和中文,比如我创建的文件夹名是 CTP-Java-SDK,路径是 D:\develop\CTP-Java-SDK。在该目录下再新建 ctp_lib 目录,将 ctpapi-6.7.11.zip 中解压出来的 8 个头文件和库文件全部复制到此目录中;在 ctp_lib 同级目录下新建 swig 目录,从 swig 的安装目录 swigwin-4.4.0\Lib\java\ 下复制 various.i 文件到此目录中,再手动创建一个 ctp.i 的空文件(swig 接口文件,后面会用到);在 ctp_lib 同级目录下新建 src\com\ctp\sdk(存放 java 文件)、wrap(存放 C++ 包装类文件)、vs_project(vs2022 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 安装目录复制
├─ src # 生成的 Java 类(自动生成)
│ └─ com
│ └─ ctp
│ └─ sdk
├─ wrap # 生成的 C++ 包装代码(自动生成)
├─ vs_project # VS2022 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,兼容 VS2022)
#include <windows.h>
#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++ 包装代码文件。
第四步:用 VS2022 编译 JNI 动态库
4.1 新建 VS DLL 项目
- 打开 VS2022,点击 '创建新项目';
- 搜索 '动态链接库 (DLL)',选择 'C++' 模板,命名为
CTPJavaDLL,保存到D:\develop\CTP_Java_SDK\vs_project;
勾选'将解决方案和项目放在同一目录中',点击 '创建'。
4.2 添加文件到项目
在 VS2022 顶部工具栏中将 Debug 改为 Release。
- 鼠标右键项目 → '添加' → '现有项':
- 添加
D:\develop\CTP_Java_SDK\wrap\ctp_wrap.cpp(C++ 包装代码); - 添加
D:\develop\CTP_Java_SDK\ctp_lib下的所有.h头文件。
- 添加
添加成功后,项目头文件和源文件会显示添加的文件。
4.3 配置项目属性(关键)
右键项目→属性


