Tips
C++ 版本 SDK 基于 C++11,不仅支持高版本 Linux 操作系统,也方便 CentOS 7 用户快速接入行情需求,无需安装复杂依赖,接入路径更简单。
1 简介
安湖行情服务是 FPGA 极速行情 接入开发包,支持 Linux 系统,以动态库(.so)和静态库 (.a) 两种形式提供给开发者。
行情用户可以通过 SDK 接口文档和 SDK 头文件完成行情接入,并对行情数据做自定义回调处理。本手册主要包含:
- 系统环境配置
- 行情 SDK 系统
- 行情 SDK 回调
本文档面向 FPGA 行情相关的开发人员、测试人员和运维人员。
2 运行环境与系统配置
本章用于在运行 SDK 或 Demo 程序前完成系统检查和基础配置。建议先按顺序检查操作系统、网络、Socket 接收缓冲区、网卡缓冲区、大页内存和 CPU 绑核规划,再进入后续配置文件修改。
2.1 接入前检查清单
| 检查项 | 建议动作 | 说明 |
|---|---|---|
| 操作系统 | 确认系统版本和内核环境 | 建议 RedHat RHEL 7.4 x64 及以上版本 |
| 网络链路 | 确认 SDK 机器和 FPGA 在相同网络环境 | 普通网卡组播和 Solarflare 模式的配置项不同 |
| Socket 缓冲区 | 检查 rmem_default 和 rmem_max | 普通网卡 UDP 接收场景建议提前调大 |
| 网卡 RX 缓冲区 | 使用 ethtool -g 检查 | 避免高峰行情下因网卡缓冲不足导致丢包 |
| 大页内存 | 使用 grep Huge /proc/meminfo 检查 | 系统运行前必须启用大页内存 |
| CPU 绑核 | 规划 recv_np 和 [dispatch] CPU 核心 | 建议接收线程尽量放在同一 NUMA 节点 |
| 配置文件 | 使用 ah_sdk_sf.conf | 请确认启动脚本加载的是交付包中的统一配置文件 |
Warning
本文档中的网卡名称、组播地址、端口、CPU 核心编号和路径均为示例。生产环境接入前,请以实际交付包、现场网络规划和运维分配的资源为准。
2.2 基础环境
- 行情 SDK 和 FPGA 在相同网络环境中的机器上运行
- RedHat RHEL 7.4 x64 及以上版本
- CPU 主频越高越好,建议预留足够 CPU 核心给接收线程和分发线程
- 可使用 Linux Onload(需要 Solarflare 网卡支持),这样能进一步降低网络延时,Onload 版本要在 8.0 以上
2.3 Socket 接收缓冲区
普通网卡 UDP 接收场景下,SDK 配置项 recv_mem_size 依赖系统 Socket 接收缓冲区内核参数。建议在运行前检查并适当调大 net.core.rmem_default 和 net.core.rmem_max,以减少 UDP 丢包风险。
查看当前系统配置:
cat /proc/sys/net/core/rmem_default
cat /proc/sys/net/core/rmem_max
临时调整如下,系统重启后失效:
sudo sysctl -w net.core.rmem_default=268435456
sudo sysctl -w net.core.rmem_max=536870912
设置永久值,需要 root 用户或者 sudo 权限编辑 /etc/sysctl.conf:
sudo vi /etc/sysctl.conf
在文件末尾添加:
net.core.rmem_default = 268435456
net.core.rmem_max = 536870912
保存后执行:
sudo sysctl -p
SDK 配置文件中的 recv_mem_size 表示 UDP 接收缓存大小。如果配置值大于等于 /proc/sys/net/core/rmem_default 的 2 倍,则行情服务会以 rmem_default * 2 为准;否则以配置文件中的 recv_mem_size 为准。Solarflare 用户态接收模式可忽略该配置项。
Tips
sysctl -w 只会临时生效,系统重启后会恢复默认值。生产环境建议写入 /etc/sysctl.conf,并由运维侧纳入主机初始化或变更记录。
2.4 网卡环形缓冲区
普通网卡组播接收时,建议检查网卡 RX 环形缓冲区。该配置通常重启后失效,如需长期生效,应由运维侧加入系统启动脚本或网卡初始化流程。
查看当前网卡环形缓冲区大小:
sudo ethtool -g 网卡名
输出中的 Pre-set maximums 表示网卡驱动和硬件支持的最大值,Current hardware settings 表示当前生效值。如果当前 RX 值不是最大值,可按实际网卡能力调整:
sudo ethtool -G 网卡名 rx 4096
2.5 大页内存
系统运行前必须启用大页内存。可先检查系统当前配置:
grep Huge /proc/meminfo
如果 HugePages_Total 大于 0,说明系统已分配大页内存。否则可设置永久值,需要 root 用户或者 sudo 权限编辑 /etc/sysctl.conf:
sudo vi /etc/sysctl.conf
在文件末尾添加:
vm.nr_hugepages=1024
保存后执行:
sudo sysctl -p
Tips
在高吞吐、低延迟的行情接收场景中,大页内存可以减少页表数量和 TLB miss,降低频繁内存访问带来的额外开销,使接收线程、分发线程和缓存队列在行情高峰时更稳定。建议结合机器内存容量、并发市场数量和队列配置预留充足的大页内存。
2.6 NUMA 与 CPU 绑核
配置文件中的 recv_np 以及 [dispatch] 下的 index_np、snapshot_np、tick_np 用于绑定接收线程和分发线程的 CPU 核心。建议在配置前先确认 NUMA 拓扑,并尽量让接收线程使用同一个 NUMA 节点下的空闲 CPU 核心。
查看系统 NUMA 硬件拓扑:
numactl --hardware
如果只有一个 NUMA 节点,可直接在该节点内规划 CPU 核心。如果存在多个 NUMA 节点,建议:
recv_np绑定的 6 个接收线程 CPU 核心尽量位于同一个 NUMA 节点[dispatch]分发线程需要 3 个 CPU 核心,可根据机器空闲情况分配- 如不需要绑核,可将对应配置设置为
-1
Warning
绑核配置应避免占用操作系统关键服务、业务主线程或其他低延迟程序已经使用的 CPU 核心。修改后建议结合日志和系统监控观察 CPU 使用率、丢包率和回调延迟。
3 行情 SDK 说明
以上部分为 SDK 的运行环境和系统配置准备工作,现在进入 行情 SDK 系统 部分,分为:
- 目录结构说明
- Demo 快速启动
3.1 目录结构
[annhoo_md_sdk_demo] tree -L 3
.
├── bin
│ ├── annhoo_md_sdk_demo
│ ├── get_version
│ └── subscribe
├── CMakeLists.txt
├── conf
│ └── ah_sdk_sf.conf
├── examples
│ ├── get_version
│ │ ├── CMakeLists.txt
│ │ └── get_version.cpp
│ └── subscribe
│ ├── CMakeLists.txt
│ └── subscribe.cpp
├── include
│ ├── annhoo_md_sdk
│ │ ├── ah_md_api.h
│ │ ├── ah_md_common.h
│ │ └── ah_md_spi.h
│ └── cmdline.h
├── lib
│ └── libannhoo_md_sdk.so
├── README.md
├── spdlog_lib
│ ├── libannhoo_md_sdk.a
│ └── libannhoo_md_sdk.so
├── inner_lib
│ ├── libannhoo_md_sdk.a
│ └── libannhoo_md_sdk.so
├── src
│ ├── ah_md_spi_inl.cpp
│ ├── ah_md_spi_inl.h
│ └── main.cpp
├── start.sh
├── stop.sh
└── tick_bulk_sf
└── inner_lib
└── libannhoo_md_sdk.so
目录结构详情:
bin: demo的二进制 生成目录bin/annhoo_md_sdk_demo: 二进制可执行文件, 行情可落地文件bin/get_version: 二进制可执行文件, 可得到sdk的版本信息: 编译时间,commit hash,分支等信息bin/subscribe: 二进制可执行文件, 演示订阅功能, 所订阅的行情输出到控制台CMakeLists.txt: demo 的 CMake 文件conf: sdk 的 配置文件目录conf/ah_sdk_sf.conf: sdk配置文件include: sdk 所依赖的 头文件目录include/ah_md_spi.h: 用户需要实现的 行情的四个回调接口文件include/ah_md_api.h: sdk 的 行情实现接口文件include/ah_quotes_common.h: sdk 的 行情数据结构文件, 主要定义了四种行情的数据结构和其他数据结构lib: sdk 的 库文件目录lib/libannhoo_md_sdk.a: 静态库 文件lib/libannhoo_md_sdk.so: 动态库 文件src: demo 源码目录ah_md_spi_inl.h: 用户自定义实现的 行情的四个回调函数 的头文件ah_md_spi_inl.cpp: 用户自定义实现的 行情的四个回调函数 的源文件main.cpp: sdk 调用样例examples/get_version.cpp: sdk 调用样例源码, 获取版本信息examples/subscribe.cpp: sdk 调用样例源码, 订阅功能演示start.sh: 一键启动脚本stop.sh: 一键停止脚本
Warning
目前 SDK 自带两套日志库:一套基于 spdlog,另一套为非 spdlog 版本。由于 spdlog 初始化不能同时存在于 SDK 和应用中,如果应用自身也使用 spdlog,请选择非 spdlog 日志版本。
3.2 Demo 快速启动
如果您需要使用 Demo 程序,可先编译源码,再修改配置文件并运行启动脚本。推荐按“编译、依赖检查、配置、运行、日志确认”的顺序操作。
3.2.1 编译
cd project_path
mkdir build
cd build
cmake ..
make
编译完成后,bin 目录下会生成可执行程序 annhoo_md_sdk_demo。
3.2.2 检查动态库依赖
运行前建议先检查程序依赖的动态库:
ldd ./bin/annhoo_md_sdk_demo
如果提示缺少 libfmt.so.*,可按操作系统版本安装 fmt:
# CentOS 7
sudo yum install -y fmt
# Rocky 9
sudo dnf install -y fmt
如果运行环境无法在线安装,也可通过源码编译安装:
unzip fmt-x.x.x.zip
cd fmt-x.x.x
mkdir build && cd build
cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local ..
make -j$(nproc)
sudo make install
sudo ldconfig
也可使用安湖提供的对应系统 .so 文件,复制到 /usr/local/lib/ 后建立软链接并执行 sudo ldconfig。
3.2.3 修改配置文件
程序运行前,请先修改交付包中的默认配置文件 conf/ah_sdk_sf.conf。重点检查以下参数:
interface:接收行情的网卡名称,Solarflare 模式填写对应网卡名,普通网卡模式按配置说明设置host/imr_interface:组播地址和接收数据的网卡 IPrecv_np:接收线程绑定的 CPU 核心索引[dispatch]:分发线程绑定的 CPU 核心索引recv_mem_size:普通网卡 UDP 接收缓存大小,配置前请先完成 Socket 接收缓冲区 设置
双市场运行时,请分别检查沪市和深市相关参数。
Tips
Solarflare 模式下,[sh] 和 [sz] 的 interface 请填写实际网卡名。沪深两市共用同一块 Solarflare 网卡时,两处可填写同一个网卡名。
3.2.4 运行
配置文件修改完成后,执行启动脚本:
./start.sh
启动后请查看 logs 目录中的日志,确认 SDK 运行模式、配置加载结果和行情流量是否正常。
3.2.5 启动后确认
启动成功不代表行情已经正常接收。建议重点确认以下信息:
- 日志中是否打印 SDK 版本、配置文件路径和配置加载结果
- 日志中是否显示普通网卡模式或 Solarflare
EFVI_mode - 沪市、深市的组播地址、端口和接收网卡是否与现场配置一致
- 行情回调是否持续触发,行情流量是否长时间为零
- 如开启绑核,接收线程和分发线程是否绑定到预期 CPU 核心
4 行情 SDK 回调
行情 SDK 的回调主要包含开发流程、配置说明和各个接口说明。
4.1 开发流程
- 调用
NewMdApi创建IAHMdApiSDK 行情 API 接口 - 按照自己的业务从
IAHMdSpi派生出一个子类,并实现自己关心的回调接口(如委托行情回调OnTickCB等),方法可参考src/ah _md_spi_inl.cpp和src/ah_md_spi_inl.h中的AHMdSpiInl实现代码. - 调用
IAHMdApi::RegisterSpi接口,把第二步所生成的自定义回调对象注册到IAHMdApi中 - 调用
IAHMdApi::SubscribeXXX接口订阅所关心的行情数据 - 调用
IAHMdApi::Start启动行情服务 - 调用
IAHMdApi::Join等待行情服务结束 - 调用
IAHMdApi::UnRegisterSpi注销行情回调, 释放行情回调和行情接口
4.2 配置说明
SDK 统一使用 conf/ah_sdk_sf.conf 作为配置文件。修改配置后,请重点检查市场类型、接收网卡、组播地址、端口、CPU 绑核和日志输出路径是否符合现场环境。
4.2.1 配置文件
[quotes_sdk_demo]$ cat conf/ah_sdk_sf.conf
[market]
markets_type=sz,sh #市场类型:sh-上海;sz-深圳
recv_type=0 #行情推送方式:0-salarflare的组播(当前支持的网卡组播);
queue_size=8192 #队列深度
recv_mem_size = 209715200
nice=-20 #进程优先级
trans_size=256
#沪市
[sh]
interface=ens1f1 #网卡名称
[sh_udp]
host=192.168.9.7 #接收数据的网卡ip
timeout=99999 #
try_count=99999 #
imr_interface=231.8.9.55 #上海行情数据组播ip
#沪市指数行情参数
[sh_index]
port=62284 #端口
recv_np=1 #绑定cpu核心的索引; -1 不绑定
#沪市集中竞价交易业务参数
[sh_snapshot]
port=62285 #端口
recv_np=3 #绑定cpu核心的索引号; -1 不绑定
#沪市逐笔委托逐笔成交业务参数
[sh_tick]
port=62286 #端口
recv_np=5 #绑定cpu核心的索引号; -1 不绑定
#深市
[sz]
interface=ens1f1 #网卡名称
[sz_udp]
host=192.168.9.7 #接收数据的网卡ip
timeout=99999 #
try_count=99999 #
imr_interface=231.8.9.55 #深市行情数据组播ip
#深市指数行情参数
[sz_index]
port=62281 #端口
recv_np=2 #绑定cpu核心的索引; -1 不绑定
#深市集中竞价交易业务参数
[sz_snapshot]
port=62282 #端口
recv_np=4 #绑定cpu核心的索引号; -1 不绑定
#深市逐笔委托逐笔成交业务参数
[sz_tick]
port=62283 #端口
recv_np=6 #绑定cpu核心的索引号; -1 不绑定
#分发线程绑定cpu核心
[dispatch]
index_np=11
snapshot_np=12
tick_np=13
[logger]
name=ah_sdk_sf.log
#0-trace;1-debug;2-info;3-warn;4-error;5-fatal;6-off;
level=2
rotate_size=1000 #单位MB
rotate_total=5
stdout=false
print_count=true
markets_type: 市场类别,其中sh表示上海,sz表示深圳,可按需配置为sz,shrecv_type: 行情推送方式,0表示 Solarflare 组播接收queue_size: 缓存队列深度,数值越大占用内存越多,缓存能力也越大recv_mem_size: UDP 接收缓存大小,主要用于普通网卡 UDP 接收场景;Solarflare 用户态接收模式可忽略该配置项nice: 进程优先级,需要 sudo 权限,Linux 取值范围为[-20, 19]trans_size: 单次传输或批量处理相关参数,请以交付版本说明为准interface: 接收行情的网卡名称,沪市配置在[sh],深市配置在[sz]host: 接收数据的网卡 IPimr_interface: 行情数据组播 IP,沪市、深市分别在[sh_udp]、[sz_udp]中配置port: 每类行情的监听端口,指数、集中竞价和逐笔行情分别配置在*_index、*_snapshot、*_tick中recv_np: 每类行情接收线程绑定的 CPU 核心索引,-1表示不绑核index_np: 指数行情分发线程绑定的 CPU 核心索引snapshot_np: 集中竞价行情分发线程绑定的 CPU 核心索引tick_np: 逐笔委托和逐笔成交分发线程绑定的 CPU 核心索引name: 日志名称level: 所要输出的日志级别rotate_size: 日志文件大小,单位为 MBrotate_total: 日志文件个数stdout: 日志是否打印屏幕, 需要注意的是,如果sdk采用的是非spdlog日志的 inner_lib 版本,那么此项开关是互斥的,即 打屏幕后不再同时打印日志文件.print_count: 是否打印行情接收统计
4.2.2 SDK 日志配置
目前的 SDK 日志有:
- 调试日志: 1 - debug
- 一般日志: 2 - info
- 警告日志: 3 - warn
- 错误日志: 4 - error
- 致命日志: 5 - fatal(该日志, 程序不正常, 必须停止排查)
- 关闭所有日志: 6 - off
4.2.3 recv_mem_size
recv_mem_size 对应 SDK 配置文件中的 UDP 接收缓存设置,主要用于普通网卡 UDP 接收场景。该配置依赖 Linux 系统的 Socket 接收缓冲区参数,配置前请先完成 Socket 接收缓冲区 检查。
行情服务系统配置文件的 recv_mem_size 所设的值, 如果大于等于 /proc/sys/net/core/rmem_default 的 2倍, 则行情服务会以 rmem_default*2 为准. 反之, 则以所设的 recv_mem_size 为准。Solarflare 用户态接收模式可忽略该配置项。
4.3 接口说明
接口函数主要是行情 sdk c++的 API 接口, 主要有
NewMdApi- 创建行情接口GetMdApiVersion- 获取 API 版本号RegisterSpi/UnRegisterSpi- 注册/注销 行情回调SubscribeXXX/UnSubscribeXXX- 订阅/取消订阅Start- 启动行情Join- 等待行情结束
需要注意的是,以下接口以实际源码为准。
4.3.1 NewMdApi - 创建行情接口
Synopsis
annhoo::IAHMdApi* NewMdApi(const char* config_path);
Parameters
| name | data type | description |
|---|---|---|
| config_path | char* const | 配置文件路径,例如 conf/ah_sdk_sf.conf |
Return
IAHMdApi pointer
4.3.2 GetMdApiVersion - 获取版本号接口
Synopsis
const char* GetMdApiVersion();
Parameters
void
Return
version: 1.1.2
4.3.3 GetMdApiVersionMore - 获取更多的版本信息
Synopsis
const char* GetMdApiVersionMore();
Parameters
void
Return
version: 1.1.2
branch: tick_bulk_sf
commit id: ddefcbc1449b2a3f10c88974bb585425ac18cf8c_2023-05-07 10:25:39 +0800
build time: 2023-05-07 10:27:27
build type: RelWithDebInfo
4.3.4 RegisterSpi - 注册回调接口
Synopsis
virtual void RegisterSpi(IAHMdSpi* imp) = 0;
Parameters
| name | data type | description |
|---|---|---|
| imp | IAHMdSpi* | 向API注册自定义的回调实现子类的对象 |
Return
void
4.3.5 UnRegisterSpi 取消回调接口
Synopsis
virtual void UnRegisterSpi(IAHMdSpi* imp) = 0;
Parameters
| name | data type | description |
|---|---|---|
| imp | IAHMdSpi* | 向API取消注册的自定义回调实现子类的对象 |
Return
void
4.3.6 SubscribeXXX - 订阅指定的行情列表
Synopsis
virtual int SubscribeIndex(const std::vector<std::string>& securityIDs) = 0;
virtual int SubscribeTick(const std::vector<std::string>& securityIDs) = 0;
virtual int SubscribeSnapshot(const std::vector<std::string>& securityIDs) = 0;
Parameters
| name | data type | description |
|---|---|---|
| securityIDs | const std::vector<std::string>& | 订阅指定的具体行情列表 |
Return
void
4.3.7 Subscribe(XXX)All - 订阅所有行情
Synopsis
virtual void SubscribeAll(void) = 0;
virtual void SubscribeIndexAll(void) = 0;
virtual void SubscribeTickAll(void) = 0;
virtual void SubscribeSnapshotAll(void) = 0;
Parameters
void
Return
void
4.3.8 UnSubscribeXXX - 取消指定的订阅行情列表
Synopsis
virtual int UnSubscribeIndex(const std::vector<std::string>& securityIDs) = 0;
virtual int UnSubscribeTick(const std::vector<std::string>& securityIDs) = 0;
virtual int UnSubscribeSnapshot(const std::vector<std::string>& securityIDs) = 0;
Parameters
| name | data type | description |
|---|---|---|
| securityIDs | const std::vector<std::string>& | 订阅具体的行情列表 |
Return
int
4.3.9 UnSubscribe(XXX)All - 取消所有订阅的行情
Synopsis
virtual void UnSubscribeAll(void) = 0;
virtual void UnSubscribeIndexAll(void) = 0;
virtual void UnSubscribeTickAll(void) = 0;
virtual void UnSubscribeSnapshotAll(void) = 0;
Parameters
void
Return
void
4.3.10 ExportSubscribeXXXX - 导出订阅列表
Synopsis
virtual void ExportSubscribeAll(std::vector<std::string>& outs) = 0;
virtual void ExportSubscribeIndex(std::vector<std::string>& outs) = 0;
virtual void ExportSubscribeTick(std::vector<std::string>& outs) = 0;
virtual void ExportSubscribeSnapShot(std::vector<std::string>& outs) = 0;
Parameters
| name | data type | description |
|---|---|---|
| outs | std::vector<std::string>& | 导出订阅的具体行情列表 |
Return
void
4.3.11 OnIndexCB - 指数行情快照回调
Synopsis
virtual void OnIndexCB(AHIndex* index)
Parameters
| name | data type | description |
|---|---|---|
| index | AHIndex* | 用户自定义实现的指数行情快照回调 |
Return
void
指数回调的时候, 参数类型为原生指针
AHIndex*
4.3.12 OnTickCB - 逐笔委托和逐笔成交回调
Synopsis
virtual void OnTickCB(AHTick* data, uint16_t type)
Parameters
| name | data type | description |
|---|---|---|
| trade | AHTick* | 用户自定义实现的逐笔委托和逐笔成交回调 |
| type | AType | 委托:192, 成交:191 |
Return
void
AHTick 是一个逐笔委托指针和逐笔成交指针的联合体, 后面有 type 标识数据类型
4.3.13 OnSnapshotCB - 集中竞价交易业务行情快照回调
Synopsis
virtual void OnSnapshotCB(AHSnapShot* snapshot)
Parameters
| name | data type | description |
|---|---|---|
| snap_shot | AHSnapshot* | 用户自定义实现的集中竞价交易业务行情快照回调回调 |
Return
void
快照回调的时候, 参数类型为原生指针
AHSnapshot*
4.4 数据结构说明
安湖行情系列产品,对于交易所标准行情数据,采用的是沪深交易所标准,只有对于个别字段考虑到系统的设计要求会有一些特殊处理。
对于传输层细节部分,可参考安湖SDK文档高阶 https://mdsdk.annhoo.cn/structannhoo_1_1_a_h_index.html
4.4.1 数据头定义(AHDataHeader)
struct AHDataHeader {
uint16_t MsgType; /*!< Quotes message type: 指数: 9011, 委托: 192, 成交: 191, 快照: 111,上海tick行情:193 */
char ExchangeID[2]; /*!< Exchange identify: SZ or SH */
};
MsgType 详情:
- 指数: 9011
- 委托: 192
- 成交: 191
- 上海Tick: 193 【上海交易所合并了逐笔委托和成交后的消息类型】
- 快照: 111
4.4.2 指数行情定义(AHIndex)
消息类型:9011 消息长度:106 数据结构:
struct AHIndex {
AHDataHeader header; /*!< Common header: MsgType: 9011, MsgLength: 100 */
char MDStreamID[4]; /*!< 深市行情类别,沪市缺失 */
union {
int64_t OrigTime; /*!< 数据生成时间 */
int64_t TradeTime; /*!< 沪市成交时间 */
};
uint16_t ChannelNo; /*!< 深市频道代码, 沪市缺失 */
char TradingPhaseCode[2]; /*!< 深市产品所处的交易阶段代码, 沪市缺失 */
char SecurityID[8]; /*!< 证券代码, format: 'xxxxxx '(end with two spaces) */
int64_t LastIndex; /*!< 最新价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t PreCloseIndex; /*!< 前收盘指数, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t OpenIndex; /*!< 今开盘, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t HighIndex; /*!< 最高价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t LowIndex; /*!< 最低价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t CloseIndex; /*!< 今收盘, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t TotalVolumeTrade; /*!< 成交总量 */
int64_t TotalValueTrade; /*!< 成交总金额, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t NumTrades; /*!< 深市成交笔数, 沪市缺失 */
};
模拟数据案例展示:
| key | val |
|---|---|
| msgtype | 9011 |
| ExchangeID | SZ |
| MDStreamID | 900 |
| OrigTime | 20220517094612000 |
| ChannelNo | 10 |
| TradingPhaseCode | T |
| SecurityID[8] | 399284 |
| LastIndex | 2608677400 |
| PreCloseIndex | 2609506600 |
| OpenIndex | 2611665800 |
| HighIndex | 2623839000 |
| LowIndex | 2604848600 |
| CloseIndex | 0 |
| TotalVolumeTrade | 10604617000 |
| TotalValueTrade | 20248596531300 |
| NumTrades | 169946 |
4.4.3 Tick行情定义(AHTick)
消息类型:tick 消息长度:76 数据结构:
struct AHTick {
AHDataHeader header; /*!< Command header, MsgType: 191/192/193 */
union {
char MDStreamID[4]; /*!< 深市的行情类别 */
uint16_t OrderIndex; /*!< 沪市的委托序号,从1开始,按channel连续 */
uint16_t TradeIndex; /*!< 沪市的成交序号,从1开始,按channel连续 */
char TickBSFlag[4]; /*!< 沪市逐笔:逐笔标识。委托:B=买单,S=卖单;产品状态订单:START=启动,
OCALL=开市集合竞价, TRADE=连续自动撮合, SUSP=停牌, CCALL=收盘集合竞价, CLOSE=闭市,
ENDTR=交易结束 */
};
union {
uint16_t ChannelNo; /*!< 深市频道代码 */
uint16_t Channel; /*!< 沪市委托通道。沪市逐笔通道 */
uint16_t TradeChannel; /*!< 沪市成交通道 */
};
union {
char Side; /*!< 深市委托时为买卖方向: 1=买, 2=卖, G=借入 F=出借, 深市成交缺失 */
char OrderBSFlag; /*!< 沪市委托时为:B=买单, S=卖单 */
char TradeBSFlag; /*!< 沪市成交为:B=外盘主动买,S=内盘主动卖,N=未知。沪市逐笔此字段已前置,故空 */
};
union {
char OrdType; /*!< 深市委托时订单类型时:1=市价,2=限价,U=本方最优 */
char OrderType; /*!< 沪市委托时订单类型时:A=新增委托订单,D=删除委托订单; 沪市成交缺失 */
char ExecType; /*!< 深市成交时订单类型时:4=撤销,F=成交 */
char Type; /*!< 沪市逐笔:A=新增委托订单,D=删除委托订单,S=产品状态订单,T=成交 */
};
union {
int64_t ApplSeqNum; /*!< 深市时,消息记录号从 1 开始计数*/
int64_t BizIndex; /*!< 沪市时,业务序列号与竞价逐笔成交消息合并后的连续编号,从 1 开始,按Channel 连续 */
};
char SecurityID[8]; /*!< 证券代码 */
union {
int64_t Price; /*!< 深市委托价格, 实际值除以10000。沪市逐笔价格, 实际值除以1000 */
int64_t OrderPrice; /*!< 沪市委托价格, 实际值除以1000 */
int64_t LastPx; /*!< 深市成交价格, 实际值除以10000 */
int64_t TradePrice; /*!< 沪市成交价格, 实际值除以1000 */
};
union {
int64_t OrderQty; /*!< 深市委托数量, 除以100 */
int64_t Balance; /*!< 沪市委托数量, 除以1000 */
int64_t LastQty; /*!< 深市成交数量, 实际值除以100 */
int64_t TradeQty; /*!< 沪市成交数量, 实际值除以1000 */
int64_t Qty; /*! 沪市逐笔数量, 实际值除以100 */
};
union {
int64_t TransactTime; /*!< 深市委托/成交时间 */
int64_t OrderTime; /*!< 沪市委托时间 */
int64_t TradeTime; /*!< 沪市成交时间 */
int64_t TickTime; /*!< 沪市逐笔订单时间或者成交时间 */
};
union {
int64_t OrderNO; /*!< 沪市委托原始订单号 */
int64_t BidApplSeqNum; /*!< 深市成交, 买方委托索引从 1 开始计数,0 表示无对应委托 */
int64_t TradeBuyNo; /*!< 沪市成交, 买方订单号 */
int64_t BuyOrderNo; /*!< 沪市逐笔买方订单 */
};
union {
int64_t OfferApplSeqNum; /*!< 深市成交, 卖方委托索引从 1 开始计数,0 表示无对应委托 */
int64_t TradeSellNo; /*!< 沪市成交, 卖方订单号 */
int64_t SellOrderNo; /*!< 沪市逐笔买方订单 */
};
int64_t TradeMoney; /*!< 深市缺失,沪市成交金额, 实际值除以 100000。沪市逐笔:如果市新增委托(Type:
A):已成交的委托数量(精度为三位), 如果是成交(Type: T):成交金额(精度为五位) */
};
4.4.4 Tick深市回调数据案例:
深市委托行情数据案例: 消息类型:192
| key | val |
|---|---|
| msgtype | 192 |
| ExchangeID | SZ |
| MDStreamID | 011 |
| ChannelNo | 2013 |
| Side | 1 |
| OrdType | 2 |
| ApplSeqNum | 23293311 |
| SecurityID[8] | 002256 |
| Price | 26200 |
| OrderQty | 90000 |
| TransactTime | 20220517132752710 |
委托价:2.62元,买入900股
深市成交行情数据案例: 消息类型:191
| key | val |
|---|---|
| msgtype | 191 |
| ExchangeID | SZ |
| MDStreamID | 011 |
| ChannelNo | 2013 |
| ExecType | F |
| ApplSeqNum | 23294075 |
| SecurityID[8] | 003040 |
| LastPx | 199100 |
| LastQty | 80000 |
| TransactTime | 20220517132753170 |
| BidApplSeqNum | 23294074 |
| OfferApplSeqNum | 13737530 |
最近委托价为:19.19元,成交量:800股
4.4.5 Tick沪市行情回调数据案例
沪市逐笔行情数据案例: 消息类型:193
| key | val |
|---|---|
| MsgType | 193 |
| ExchangeID | SH |
| TickBSFlag | B |
| Channel | 6 |
| Type | T |
| BizIndex | 8121272 |
| SecurityID | 600455 |
| Price | 16630 |
| Qty | 100000 |
| TickTime | 20240417101502980 |
| BuyOrderNo | 5134670 |
| SellOrderNo | 5134492 |
| TradeMony | 166300000 |
逐笔价格:16.63元,逐笔数量:100股。
4.4.6 快照行情定义(AHSnapshot)
消息类型:111 消息长度:514 数据结构:
struct AHSnapshot {
AHDataHeader header; /*!< Command header, MsgType: 111, MsgType: 514 */
char MDStreamID[4]; /*!< 深市行情类别,沪市缺失 */
union {
int64_t OrigTime; /*!< 数据生成时间 */
int64_t TradeTime; /*!< 沪市成交时间 */
};
uint16_t ChannelNo; /*!< 深市频道代码, 沪市缺失 */
char InstrumentStatus[2]; /*!< 沪市当前品种交易状态, 深市缺失 */
char TradingPhaseCode[4]; /*!< 当前产品状态 */
char SecurityID[8]; /*!< 证券代码, format: 'xxxxxx '(end with two spaces) */
int64_t BidPrice[10]; /*!< 十档申买价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t AskPrice[10]; /*!< 十档申卖价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t BidVolume[10]; /*!< 十档申买量 */
int64_t AskVolume[10]; /*!< 十档申卖量 */
int64_t PrevClosePx; /*!< 昨收价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t OpenPx; /*!< 今开盘, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t HighPx; /*!< 最高价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t LowPx; /*!< 最低价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t LastPx; /*!< 最新价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t ClosePx; /*!< 收盘价, 深市缺失, 沪市除以1000后为分 */
int64_t NumTrades; /*!< 成交笔数 */
int64_t TotalVolumeTrade; /*!< 成交总量 */
int64_t TotalValueTrade; /*!< 成交总金额 */
int64_t TotalBidQty; /*!< 委托买入总量 */
int64_t WeightedAvgBidPx; /*!< 加权平均委托买入价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t TotalOfferQty; /*!< 委托卖出总量 */
int64_t WeightedAvgOfferPx; /*!< 加权平均委托卖出价, 深市除以10000后为分, 沪市除以1000后为分 */
int64_t IOPV; /*!< 基金实时参考净值 */
int64_t LPV; /*!< 基金 T-1 日净值, 沪市缺失 */
int64_t UpperLimitPrice; /*!< 涨停价, 深市除以10000后为分, 沪市缺失 */
int64_t LowerLimitPrice; /*!< 跌停价, 深市除以10000后为分, 沪市缺失 */
int64_t OpenInterest; /*!< 合约持仓量, 沪市缺失 */
int64_t LastSubPrevClosePrice; /*!< 升跌一, 沪市缺失 */
int64_t LastSubPrevLastPrice; /*!< 升跌二, 沪市缺失 */
};
模拟环境下,案例数据展示:
| key | val |
|---|---|
| MsgType | 111 |
| ExchangeID | SZ |
| MDStreamID | 010 |
| Time | 20220517131115000 |
| ChannelNo | 1012 |
| TradingPhaseCode | T0 |
| SecurityID | 300994 |
| BidPrice[0] | 29960000 |
| BidPrice[1] | 29950000 |
| BidPrice[2] | 29940000 |
| BidPrice[3] | 29920000 |
| BidPrice[4] | 29910000 |
| BidPrice[5] | 29900000 |
| BidPrice[6] | 29870000 |
| BidPrice[7] | 29850000 |
| BidPrice[8] | 29800000 |
| BidPrice[9] | 29740000 |
| AskPrice[0] | 29990000 |
| AskPrice[1] | 30000000 |
| AskPrice[2] | 30010000 |
| AskPrice[3] | 30040000 |
| AskPrice[4] | 30050000 |
| AskPrice[5] | 30060000 |
| AskPrice[6] | 30080000 |
| AskPrice[7] | 30100000 |
| AskPrice[8] | 30120000 |
| AskPrice[9] | 30170000 |
| BidVolume[0] | 220000 |
| BidVolume[1] | 10000 |
| BidVolume[2] | 30000 |
| BidVolume[3] | 310000 |
| BidVolume[4] | 30000 |
| BidVolume[5] | 10000 |
| BidVolume[6] | 6170000 |
| BidVolume[7] | 520000 |
| BidVolume[8] | 50000 |
| BidVolume[9] | 40000 |
| AskVolume[0] | 100000 |
| AskVolume[1] | 330000 |
| AskVolume[2] | 320000 |
| AskVolume[3] | 330000 |
| AskVolume[4] | 10000 |
| AskVolume[5] | 10000 |
| AskVolume[6] | 10000 |
| AskVolume[7] | 150000 |
| AskVolume[8] | 10000 |
| AskVolume[9] | 50000 |
| PrevClosePx | 297100 |
| OpenPx | 29300000 |
| HighPx | 29980000 |
| LowPx | 28850000 |
| LastPx | 29980000 |
| ClosePx | 0 |
| NumTrades | 4971 |
| TotalVolumeTrade | 169965300 |
| TotalValueTrade | 497509300900 |
| TotalBidQty | 77280000 |
| WeightedAvgBidPx | 29110000 |
| TotalOfferQty | 29550000 |
| WeightedAvgOfferPx | 32070000 |
| IOPV | 0 |
| LPV | 0 |
| UpperLimitPrice | 35650000 |
| LowerLimitPrice | 23770000 |
| OpenInterest | 0 |
| LastSubPrevClosePrice | 270000 |
| LastSubPrevLastPrice | 30000 |
4.5 字典说明
4.5.1 TradingPhaseCode
产品所处的交易阶段代码
深交所:
- 第 0 位:S=启动(开市前),O=开盘集合竞价,T=连续竞价,B=休市,C=收盘集合竞价,E=已闭市,H=临时停牌,A=盘后交易,V=波动性中断
- 第 1 位:0=正常状态,1=全天停牌
上交所: 该字段为 8 位字符串,左起每位表示特定的含义,无定义则填空格
- 第 1 位:‘S’表示启动(开市前)时段,‘C’表示开盘集合竞价时段,‘T’表示连续交易时段,‘E’表示闭市时段,‘P’表示产品停牌,‘M’表示可恢复交易的熔断时段(盘中集合竞价),‘N’表示不可恢复交易的熔断时段(暂停交易至闭市),‘U’表示收盘集合竞价时段。
- 第 2 位:‘0’表示此产品不可正常交易,‘1’表示此产品可正常交易,无意义填空格。
- 第 3 位:‘0’表示未上市,‘1’表示已上市。
- 第 4 位:‘0’表示此产品在当前时段不接受订单申报,‘1’ 表示此产品在当前时段可接受订单申报。无意义填空格
5 其他
配置文件检查
修改 conf/ah_sdk_sf.conf 后,建议先确认配置文件路径、市场类型、网卡名称、组播地址、端口和 CPU 绑核配置,再启动 SDK。
Tips
生产环境中,网卡名称、组播地址、端口和 CPU 核心编号通常由现场网络和运维资源规划决定。配置变更后建议保留变更记录,并结合启动日志确认实际加载的配置文件路径。
6 常见问题
编译失败如何处理? 编译前请检查主目录下是否已存在
build目录。如存在历史编译文件,可清理build目录后重新执行编译命令。还需确认cmake、编译器版本和动态库依赖是否满足要求。提示缺少
libfmt.so.*怎么办? 请先通过ldd ./bin/annhoo_md_sdk_demo确认缺失的动态库。联网环境可使用yum或dnf安装fmt;离线环境可源码编译安装,或使用安湖提供的对应系统.so文件并执行sudo ldconfig。启动异常如何排查? 请查看启动日志,确认配置文件是否加载成功、网卡名和组播地址是否正确、运行模式是否符合预期,以及是否存在版本或硬件驱动问题。详情可对照本手册运行环境与系统配置章节,或者参考线上文档:https://doc.annhoo.cn
行情流量持续为零如何排查? 请优先检查配置文件中的
interface、host、imr_interface、端口和市场配置是否正确,再检查组播网络、网卡环形缓冲区、Socket 接收缓冲区以及上游行情是否正常。如编译过程和参数配置无误仍无法正常运行,请联系技术人员。如何确定自己的SDK运行模式? 当客户使用我们的sdk运行后,启动日志中是有对应的运行日志记录的。如果是采用SF的ef_vi模式接收,在日志中会存在记录:EFVI_mode for tick market之类的数据。
修订记录
| 版本 | 日期 | 备注 |
|---|---|---|
| 0.0.1 | 2024-9-20 | 对原有的pro版本进行c++ 11重构,支持centos7.6版本用户快速接入 |
| 1.0.1 | 2024-10-08 | 对性能进行优化处理 |
| 2.0.1 | 2025-6-08 | 对Solarflare网卡的ef_vi特性进行重构 |
| 2.1.0 | 2025-8-12 | 对部分数据结构进行更新和优化 |
| 3.0.1 | 2026-5-08 | 升级整个硬件系统的通讯组件,进一步降低通信延迟 |
| 4.0.1 | 2026-6-15 | 对SDK部分的依赖进行优化,减少客户端对外部组件的依赖 |
