# 沪深 Level-2 行情 C++ SDK 接入指南 来源:https://doc.annhoo.cn/hushensdk/lite.html 摘要:安湖沪深 Level-2 C++11 SDK 使用手册,涵盖 Linux 环境、Solarflare X2522 与 Onload 8.0+、Demo 编译、行情订阅、回调接口及丢包排查。 提示 C++ 版本 SDK 基于 C++11,不仅支持高版本 Linux 操作系统,也方便 CentOS 7 用户快速接入行情需求,无需安装复杂依赖,接入路径更简单。 安湖行情服务是 FPGA 极速行情 接入开发包,支持 Linux 系统,以动态库(.so)和静态库 (.a) 两种形式提供给开发者。 行情用户可以通过 SDK 接口文档和 SDK 头文件完成行情接入,并对行情数据做自定义回调处理。本手册主要包含: 系统环境配置 行情 SDK 系统 行情 SDK 回调 本文档面向 FPGA 行情相关的开发人员、测试人员和运维人员。 本章用于在运行 SDK 或 Demo 程序前完成系统检查和基础配置。建议先按顺序检查操作系统、网络、Socket 接收缓冲区、网卡缓冲区、大页内存和 CPU 绑核规划,再进入后续配置文件修改。 本 SDK 支持以下两种行情接收模式,请根据现场网卡条件和时延、稳定性要求选择: 普通网卡模式 配置优化后即可接入 无需安装 Onload 软件栈 按本文档完成 Socket 缓冲区、网卡参数、大页内存和 CPU 绑核等优化配置 高峰行情下受普通网卡和内核网络路径限制,可能出现丢包 Solarflare 网卡模式 Onload 8.0+ 极速接收 必须安装与网卡及 SDK 匹配的 Solarflare Onload 软件栈 Onload 版本要求为 8.0 及以上 适合对低延迟和收包稳定性有更高要求的生产场景 使用脚本快速检查 安湖提供了环境检查脚本,方便用户按本章检查项进行测试。请前往沪深 SDK 环境检查下载脚本,查看运行方法、操作系统适用范围和结果说明,再根据提示完成环境配置。脚本只读取配置,不会修改系统参数。 检查项 | 建议动作 | 说明 | 操作系统 | 确认发行版、版本、架构和内核环境 | 推荐 RedHat RHEL 7.4 x64 及以上;已适配 CentOS 7.9、Rocky Linux 8.5+、Ubuntu 22.04、Ubuntu 24.04.4 LTS。使用 Onload 8.0+ 时,请确认内核满足对应版本要求 | 网络链路 | 确认 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 | 请确认启动脚本加载的是交付包中的统一配置文件 | 注意 本文档中的网卡名称、组播地址、端口、CPU 核心编号和路径均为示例。生产环境接入前,请以实际交付包、现场网络规划和运维分配的资源为准。 提示 操作系统版本不仅影响 SDK 的安装和运行,也决定 Onload 能否启用新版本提供的内核加速能力。Onload 8.0 及以上版本基于较新的 Linux 内核特性优化数据包处理路径,可降低网络处理开销并提升低延迟场景下的收包性能。接入前请记录 cat /etc/os-release、uname -r 和 onload --version 的输出,并按照交付包中的 Onload 版本升级记录核对发行版、内核与网卡驱动的组合;不满足要求时应先升级或联系技术支持确认。 行情 SDK 和 FPGA 在相同网络环境中的机器上运行 推荐 RedHat RHEL 7.4 x64 及以上版本;目前已适配 CentOS 7.9、Rocky Linux 8.5+、Ubuntu 22.04、Ubuntu 24.04.4 LTS CPU 主频越高越好,建议预留足够 CPU 核心给接收线程和分发线程 如采用 Solarflare 网卡极速接收,必须配合安湖官方行情 SDK 安装 Solarflare Onload 软件栈,版本要求为 8.0 及以上;普通网卡模式无需安装 Onload,但应完成本文档列出的优化配置 注意 安湖官方 SDK 已与 Solarflare 网卡的 Onload 软件栈(含配套驱动组件)进行深度适配,充分发挥 ef_vi 用户态高速收包能力,用户无需额外开发即可接入。生产环境请安装与网卡及 SDK 匹配的 Onload 软件栈,版本要求为 8.0 及以上,并按本文档完成相关配置检查。 普通网卡 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 用户态接收模式可忽略该配置项。 提示 sysctl -w 只会临时生效,系统重启后会恢复默认值。生产环境建议写入 /etc/sysctl.conf,并由运维侧纳入主机初始化或变更记录。 普通网卡组播接收时,建议检查网卡 RX 环形缓冲区。该配置通常重启后失效,如需长期生效,应由运维侧加入系统启动脚本或网卡初始化流程。 查看当前网卡环形缓冲区大小: sudo ethtool -g 网卡名 输出中的 Pre-set maximums 表示网卡驱动和硬件支持的最大值,Current hardware settings 表示当前生效值。如果当前 RX 值不是最大值,可按实际网卡能力调整: sudo ethtool -G 网卡名 rx 4096 系统运行前必须启用大页内存。可先检查系统当前配置: grep Huge /proc/meminfo 如果 HugePages_Total 大于 0,说明系统已分配大页内存。否则可设置永久值,需要 root 用户或者 sudo 权限编辑 /etc/sysctl.conf: sudo vi /etc/sysctl.conf 在文件末尾添加: vm.nr_hugepages=1024 保存后执行: sudo sysctl -p 提示 在高吞吐、低延迟的行情接收场景中,大页内存可以减少页表数量和 TLB miss,降低频繁内存访问带来的额外开销,使接收线程、分发线程和缓存队列在行情高峰时更稳定。建议结合机器内存容量、并发市场数量和队列配置预留充足的大页内存。 配置文件中的 recv_np 以及 [dispatch] 下的 index_np、snapshot_np、tick_np 用于绑定接收线程和分发线程的 CPU 核心。SDK 运行时必须完成 CPU 绑核,建议在配置前先确认 NUMA 拓扑,并优先使用同一个 NUMA 节点下未被其他进程占用的独立 CPU 核心。 查看系统 NUMA 硬件拓扑: numactl --hardware 如果只有一个 NUMA 节点,可直接在该节点内规划 CPU 核心。如果存在多个 NUMA 节点,建议: recv_np 绑定的 6 个接收线程 CPU 核心尽量位于同一个 NUMA 节点 [dispatch] 分发线程需要 3 个 CPU 核心,可根据机器空闲情况分配 接收线程和分发线程必须绑定 CPU 核心,优先选择未被其他进程占用的独立核心 注意 绑核配置应避免占用操作系统关键服务、业务主线程或其他低延迟程序已经使用的 CPU 核心。修改后建议结合日志和系统监控观察 CPU 使用率、丢包率和回调延迟。 以上部分为 SDK 的运行环境和系统配置准备工作,现在进入 行情 SDK 系统 部分,分为: 目录结构说明 Demo 快速启动 [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 : 一键停止脚本 注意 目前 SDK 自带两套日志库:一套基于 spdlog,另一套为非 spdlog 版本。由于 spdlog 初始化不能同时存在于 SDK 和应用中,如果应用自身也使用 spdlog,请选择非 spdlog 日志版本。 如果您需要使用 Demo 程序,可先编译源码,再修改配置文件并运行启动脚本。推荐按“编译、依赖检查、配置、运行、日志确认”的顺序操作。 cd project_path mkdir build cd build cmake .. make 编译完成后,bin 目录下会生成可执行程序 annhoo_md_sdk_demo。 运行前建议先检查程序依赖的动态库: 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。 程序运行前,请先修改交付包中的默认配置文件 conf/ah_sdk_sf.conf。重点检查以下参数: interface:接收行情的网卡名称,Solarflare 模式填写对应网卡名,普通网卡模式按配置说明设置 host / imr_interface:组播地址和接收数据的网卡 IP recv_np:接收线程绑定的 CPU 核心索引 [dispatch]:分发线程绑定的 CPU 核心索引 recv_mem_size:普通网卡 UDP 接收缓存大小,配置前请先完成 Socket 接收缓冲区 设置 双市场运行时,请分别检查沪市和深市相关参数。 提示 Solarflare 模式下,[sh] 和 [sz] 的 interface 请填写实际网卡名。沪深两市共用同一块 Solarflare 网卡时,两处可填写同一个网卡名。 配置文件修改完成后,执行启动脚本: ./start.sh 启动后请查看 logs 目录中的日志,确认 SDK 运行模式、配置加载结果和行情流量是否正常。 启动成功不代表行情已经正常接收。建议重点确认以下信息: 日志中是否打印 SDK 版本、配置文件路径和配置加载结果 日志中是否显示普通网卡模式或 Solarflare EFVI_mode 沪市、深市的组播地址、端口和接收网卡是否与现场配置一致 行情回调是否持续触发,行情流量是否长时间为零 接收线程和分发线程是否绑定到预期 CPU 核心,且所用核心是否尽量保持独立 行情 SDK 的回调主要包含开发流程、配置说明和各个接口说明。 调用 NewMdApi 创建 IAHMdApi SDK 行情 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 注销行情回调, 释放行情回调和行情接口 SDK 统一使用 conf/ah_sdk_sf.conf 作为配置文件。修改配置后,请重点检查市场类型、接收网卡、组播地址、端口、CPU 绑核和日志输出路径是否符合现场环境。 注意 CPU 绑核为必填配置,请填写 Linux 系统暴露的逻辑 CPU 编号,编号通常从 0 开始。建议为接收线程和分发线程分配未被其他进程占用的独立 CPU 核心,避免资源竞争影响收包效率并增加行情高峰期的丢包概率。实际可用的 CPU 编号请以 lscpu -e 或 /sys/devices/system/cpu/online 的输出为准。 [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核心的索引; #沪市集中竞价交易业务参数 [sh_snapshot] port=62285 #端口 recv_np=3 #绑定cpu核心的索引号; #沪市逐笔委托逐笔成交业务参数 [sh_tick] port=62286 #端口 recv_np=5 #绑定cpu核心的索引号; #深市 [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核心的索引; #深市集中竞价交易业务参数 [sz_snapshot] port=62282 #端口 recv_np=4 #绑定cpu核心的索引号; #深市逐笔委托逐笔成交业务参数 [sz_tick] port=62283 #端口 recv_np=6 #绑定cpu核心的索引号; #分发线程绑定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,sh recv_type : 行情推送方式,0 表示 Solarflare 组播接收 queue_size : 缓存队列深度,数值越大占用内存越多,缓存能力也越大 recv_mem_size : UDP 接收缓存大小,主要用于普通网卡 UDP 接收场景;Solarflare 用户态接收模式可忽略该配置项 nice : 进程优先级,需要 sudo 权限,Linux 取值范围为 [-20, 19] trans_size : 单次传输或批量处理相关参数,请以交付版本说明为准 interface : 接收行情的网卡名称,沪市配置在 [sh],深市配置在 [sz] host : 接收数据的网卡 IP imr_interface : 行情数据组播 IP,沪市、深市分别在 [sh_udp]、[sz_udp] 中配置 port : 每类行情的监听端口,指数、集中竞价和逐笔行情分别配置在 *_index、*_snapshot、*_tick 中 recv_np : 每类行情接收线程绑定的 CPU 核心索引 index_np : 指数行情分发线程绑定的 CPU 核心索引 snapshot_np : 集中竞价行情分发线程绑定的 CPU 核心索引 tick_np : 逐笔委托和逐笔成交分发线程绑定的 CPU 核心索引 name : 日志名称 level : 所要输出的日志级别 rotate_size : 日志文件大小,单位为 MB rotate_total : 日志文件个数 stdout : 日志是否打印屏幕, 需要注意的是,如果sdk采用的是非spdlog日志的 inner_lib 版本,那么此项开关是互斥的,即 打屏幕后不再同时打印日志文件. print_count : 是否打印行情接收统计 目前的 SDK 日志有: 调试日志: 1 - debug 一般日志: 2 - info 警告日志: 3 - warn 错误日志: 4 - error 致命日志: 5 - fatal(该日志, 程序不正常, 必须停止排查) 关闭所有日志: 6 - off 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 用户态接收模式可忽略该配置项。 接口函数主要是行情 sdk c++的 API 接口, 主要有 NewMdApi - 创建行情接口 GetMdApiVersion - 获取 API 版本号 RegisterSpi/UnRegisterSpi - 注册/注销 行情回调 SubscribeXXX/UnSubscribeXXX - 订阅/取消订阅 Start - 启动行情 Join - 等待行情结束 需要注意的是,以下接口以实际源码为准。 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 Synopsis const char* GetMdApiVersion(); Parameters void Return version: 1.1.2 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 Synopsis virtual void RegisterSpi(IAHMdSpi* imp) = 0; Parameters name | data type | description | imp | IAHMdSpi* | 向API注册自定义的回调实现子类的对象 | Return void Synopsis virtual void UnRegisterSpi(IAHMdSpi* imp) = 0; Parameters name | data type | description | imp | IAHMdSpi* | 向API取消注册的自定义回调实现子类的对象 | Return void Synopsis virtual int SubscribeIndex(const std::vector& securityIDs) = 0; virtual int SubscribeTick(const std::vector& securityIDs) = 0; virtual int SubscribeSnapshot(const std::vector& securityIDs) = 0; Parameters name | data type | description | securityIDs | const std::vector& | 订阅指定的具体行情列表 | Return void 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 Synopsis virtual int UnSubscribeIndex(const std::vector& securityIDs) = 0; virtual int UnSubscribeTick(const std::vector& securityIDs) = 0; virtual int UnSubscribeSnapshot(const std::vector& securityIDs) = 0; Parameters name | data type | description | securityIDs | const std::vector& | 订阅具体的行情列表 | Return int 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 Synopsis virtual void ExportSubscribeAll(std::vector& outs) = 0; virtual void ExportSubscribeIndex(std::vector& outs) = 0; virtual void ExportSubscribeTick(std::vector& outs) = 0; virtual void ExportSubscribeSnapShot(std::vector& outs) = 0; Parameters name | data type | description | outs | std::vector& | 导出订阅的具体行情列表 | Return void Synopsis virtual void OnIndexCB(AHIndex* index) Parameters name | data type | description | index | AHIndex* | 用户自定义实现的指数行情快照回调 | Return void 指数回调的时候, 参数类型为原生指针 AHIndex* Synopsis virtual void OnTickCB(AHTick* data, uint16_t type) Parameters name | data type | description | trade | AHTick* | 用户自定义实现的逐笔委托和逐笔成交回调 | type | AType | 委托:192, 成交:191 | Return void AHTick 是一个逐笔委托指针和逐笔成交指针的联合体, 后面有 type 标识数据类型 Synopsis virtual void OnSnapshotCB(AHSnapShot* snapshot) Parameters name | data type | description | snap_shot | AHSnapshot* | 用户自定义实现的集中竞价交易业务行情快照回调回调 | Return void 快照回调的时候, 参数类型为原生指针 AHSnapshot* 安湖行情系列产品,对于交易所标准行情数据,采用的是沪深交易所标准,只有对于个别字段考虑到系统的设计要求会有一些特殊处理。 对于传输层细节部分,可参考安湖SDK文档高阶 https://mdsdk.annhoo.cn/structannhoo_1_1_a_h_index.html 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 消息类型: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 | 消息类型: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):成交金额(精度为五位) */ }; 深市委托行情数据案例: 消息类型: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股 沪市逐笔行情数据案例: 消息类型: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股。 消息类型: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 | 产品所处的交易阶段代码 深交所: 第 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’ 表示此产品在当前时段可接受订单申报。无意义填空格 修改 conf/ah_sdk_sf.conf 后,建议先确认配置文件路径、市场类型、网卡名称、组播地址、端口和 CPU 绑核配置,再启动 SDK。 提示 生产环境中,网卡名称、组播地址、端口和 CPU 核心编号通常由现场网络和运维资源规划决定。配置变更后建议保留变更记录,并结合启动日志确认实际加载的配置文件路径。 回答: 编译前请检查主目录下是否已存在 build 目录。如存在历史编译文件,可清理 build 目录后重新执行编译命令。还需确认 cmake、编译器版本和动态库依赖是否满足要求。 回答: 请先通过 ldd ./bin/annhoo_md_sdk_demo 确认缺失的动态库。联网环境可使用 yum 或 dnf 安装 fmt;离线环境可源码编译安装,或使用安湖提供的对应系统 .so 文件并执行 sudo ldconfig。 回答: 请查看启动日志,确认配置文件是否加载成功、网卡名和组播地址是否正确、运行模式是否符合预期,以及是否存在版本或硬件驱动问题。详情可对照本手册运行环境与系统配置章节,或者参考线上文档:https://doc.annhoo.cn 回答: 请优先检查配置文件中的 interface、host、imr_interface、端口和市场配置是否正确,再检查组播网络、网卡环形缓冲区、Socket 接收缓冲区以及上游行情是否正常。如编译过程和参数配置无误仍无法正常运行,请联系技术人员。 回答: 当客户使用我们的 SDK 运行后,启动日志中会有对应的运行日志记录。如果是采用 SF 的 ef_vi 模式接收,在日志中会存在 EFVI_mode for tick market 之类的记录。 回答: 一般情况下,数据丢包通常是 Solarflare 网卡的环形队列设置错误或者大页配置错误。请参考本手册运行环境与系统配置章节中的网卡环形缓冲区和大页内存检查事项进行排查。 如果客户的基础环境配置没有问题、各项检查均已通过,且 Solarflare 网卡及相关配置也运行正常,但仍然出现丢包,应继续从物理链路和网络设备层面排查: 检查光纤和光模块。 确认光纤没有过度弯曲、挤压或损伤,并请机房核实网卡与交换机之间的物理连接、端口状态和光模块状态,确认光模块的收发光功率处于正常范围且与交换机规格匹配。 检查交换机及交换机之间的网络。 核查交换机端口、链路和转发状态,以及交换机之间的互联网络是否正常。已有现场案例表明,交换机个别字段异常可能导致报文乱码和丢包;在多层级交换机互联的场景下,tick 行情数据更容易受到影响。此类问题属于网络和网络设备层面,应由机房或网络设备维护人员进一步检查链路质量、端口错误统计和设备配置兼容性。 版本 | 日期 | 备注 | 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部分的依赖进行优化,减少客户端对外部组件的依赖 | 4.0.2 | 2026-9-9 | 对文档中部分说明进行完善避免用户歧义,对常见问题模块进行梳理 |