安湖极速交易行情文档安湖极速交易行情文档
首页
开始
  • 客户端SDK(c++)
  • java语言版本SDK
期货市场
  • 市场行情数据
  • 量化分析数据
  • 估值定价数据
常见问题
技术支持
首页
开始
  • 客户端SDK(c++)
  • java语言版本SDK
期货市场
  • 市场行情数据
  • 量化分析数据
  • 估值定价数据
常见问题
技术支持
  • Level2 行情 C++ SDK 说明

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:组播地址和接收数据的网卡 IP
  • recv_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 开发流程

  1. 调用 NewMdApi 创建 IAHMdApi SDK 行情 API 接口
  2. 按照自己的业务从 IAHMdSpi 派生出一个子类,并实现自己关心的回调接口(如委托行情回调 OnTickCB 等),方法可参考 src/ah _md_spi_inl.cpp 和 src/ah_md_spi_inl.h 中的 AHMdSpiInl 实现代码.
  3. 调用 IAHMdApi::RegisterSpi 接口,把第二步所生成的自定义回调对象注册到 IAHMdApi 中
  4. 调用 IAHMdApi::SubscribeXXX 接口订阅所关心的行情数据
  5. 调用 IAHMdApi::Start 启动行情服务
  6. 调用 IAHMdApi::Join 等待行情服务结束
  7. 调用 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,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 核心索引,-1 表示不绑核
  • index_np : 指数行情分发线程绑定的 CPU 核心索引
  • snapshot_np : 集中竞价行情分发线程绑定的 CPU 核心索引
  • tick_np : 逐笔委托和逐笔成交分发线程绑定的 CPU 核心索引
  • name : 日志名称
  • level : 所要输出的日志级别
  • rotate_size : 日志文件大小,单位为 MB
  • rotate_total : 日志文件个数
  • stdout : 日志是否打印屏幕, 需要注意的是,如果sdk采用的是非spdlog日志的 inner_lib 版本,那么此项开关是互斥的,即 打屏幕后不再同时打印日志文件.
  • print_count : 是否打印行情接收统计

4.2.2 SDK 日志配置

目前的 SDK 日志有:

  1. 调试日志: 1 - debug
  2. 一般日志: 2 - info
  3. 警告日志: 3 - warn
  4. 错误日志: 4 - error
  5. 致命日志: 5 - fatal(该日志, 程序不正常, 必须停止排查)
  6. 关闭所有日志: 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 接口, 主要有

  1. NewMdApi - 创建行情接口
  2. GetMdApiVersion - 获取 API 版本号
  3. RegisterSpi/UnRegisterSpi - 注册/注销 行情回调
  4. SubscribeXXX/UnSubscribeXXX - 订阅/取消订阅
  5. Start - 启动行情
  6. Join - 等待行情结束

需要注意的是,以下接口以实际源码为准。

4.3.1 NewMdApi - 创建行情接口

Synopsis

annhoo::IAHMdApi* NewMdApi(const char* config_path);

Parameters

namedata typedescription
config_pathchar* 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

namedata typedescription
impIAHMdSpi*向API注册自定义的回调实现子类的对象

Return

void

4.3.5 UnRegisterSpi 取消回调接口

Synopsis

virtual void UnRegisterSpi(IAHMdSpi* imp) = 0;

Parameters

namedata typedescription
impIAHMdSpi*向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

namedata typedescription
securityIDsconst 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

namedata typedescription
securityIDsconst 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

namedata typedescription
outsstd::vector<std::string>&导出订阅的具体行情列表

Return

void

4.3.11 OnIndexCB - 指数行情快照回调

Synopsis

virtual void OnIndexCB(AHIndex* index)

Parameters

namedata typedescription
indexAHIndex*用户自定义实现的指数行情快照回调

Return

void

指数回调的时候, 参数类型为原生指针 AHIndex*

4.3.12 OnTickCB - 逐笔委托和逐笔成交回调

Synopsis

virtual void OnTickCB(AHTick* data, uint16_t type)

Parameters

namedata typedescription
tradeAHTick*用户自定义实现的逐笔委托和逐笔成交回调
typeAType委托:192, 成交:191

Return

void

AHTick 是一个逐笔委托指针和逐笔成交指针的联合体, 后面有 type 标识数据类型

4.3.13 OnSnapshotCB - 集中竞价交易业务行情快照回调

Synopsis

virtual void OnSnapshotCB(AHSnapShot* snapshot)

Parameters

namedata typedescription
snap_shotAHSnapshot*用户自定义实现的集中竞价交易业务行情快照回调回调

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;        /*!< 深市成交笔数, 沪市缺失 */
};


模拟数据案例展示:

keyval
msgtype9011
ExchangeIDSZ
MDStreamID900
OrigTime20220517094612000
ChannelNo10
TradingPhaseCodeT
SecurityID[8]399284
LastIndex2608677400
PreCloseIndex2609506600
OpenIndex2611665800
HighIndex2623839000
LowIndex2604848600
CloseIndex0
TotalVolumeTrade10604617000
TotalValueTrade20248596531300
NumTrades169946

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

keyval
msgtype192
ExchangeIDSZ
MDStreamID011
ChannelNo2013
Side1
OrdType2
ApplSeqNum23293311
SecurityID[8]002256
Price26200
OrderQty90000
TransactTime20220517132752710

委托价:2.62元,买入900股

深市成交行情数据案例: 消息类型:191

keyval
msgtype191
ExchangeIDSZ
MDStreamID011
ChannelNo2013
ExecTypeF
ApplSeqNum23294075
SecurityID[8]003040
LastPx199100
LastQty80000
TransactTime20220517132753170
BidApplSeqNum23294074
OfferApplSeqNum13737530

最近委托价为:19.19元,成交量:800股

4.4.5 Tick沪市行情回调数据案例

沪市逐笔行情数据案例: 消息类型:193

keyval
MsgType193
ExchangeIDSH
TickBSFlagB
Channel6
TypeT
BizIndex8121272
SecurityID600455
Price16630
Qty100000
TickTime20240417101502980
BuyOrderNo5134670
SellOrderNo5134492
TradeMony166300000

逐笔价格: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;  /*!< 升跌二, 沪市缺失  */
};

模拟环境下,案例数据展示:

keyval
MsgType111
ExchangeIDSZ
MDStreamID010
Time20220517131115000
ChannelNo1012
TradingPhaseCodeT0
SecurityID300994
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
PrevClosePx297100
OpenPx29300000
HighPx29980000
LowPx28850000
LastPx29980000
ClosePx0
NumTrades4971
TotalVolumeTrade169965300
TotalValueTrade497509300900
TotalBidQty77280000
WeightedAvgBidPx29110000
TotalOfferQty29550000
WeightedAvgOfferPx32070000
IOPV0
LPV0
UpperLimitPrice35650000
LowerLimitPrice23770000
OpenInterest0
LastSubPrevClosePrice270000
LastSubPrevLastPrice30000

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 常见问题

  1. 编译失败如何处理? 编译前请检查主目录下是否已存在 build 目录。如存在历史编译文件,可清理 build 目录后重新执行编译命令。还需确认 cmake、编译器版本和动态库依赖是否满足要求。

  2. 提示缺少 libfmt.so.* 怎么办? 请先通过 ldd ./bin/annhoo_md_sdk_demo 确认缺失的动态库。联网环境可使用 yum 或 dnf 安装 fmt;离线环境可源码编译安装,或使用安湖提供的对应系统 .so 文件并执行 sudo ldconfig。

  3. 启动异常如何排查? 请查看启动日志,确认配置文件是否加载成功、网卡名和组播地址是否正确、运行模式是否符合预期,以及是否存在版本或硬件驱动问题。详情可对照本手册运行环境与系统配置章节,或者参考线上文档:https://doc.annhoo.cn

  4. 行情流量持续为零如何排查? 请优先检查配置文件中的 interface、host、imr_interface、端口和市场配置是否正确,再检查组播网络、网卡环形缓冲区、Socket 接收缓冲区以及上游行情是否正常。如编译过程和参数配置无误仍无法正常运行,请联系技术人员。

  5. 如何确定自己的SDK运行模式? 当客户使用我们的sdk运行后,启动日志中是有对应的运行日志记录的。如果是采用SF的ef_vi模式接收,在日志中会存在记录:EFVI_mode for tick market之类的数据。

修订记录

版本日期备注
0.0.12024-9-20对原有的pro版本进行c++ 11重构,支持centos7.6版本用户快速接入
1.0.12024-10-08对性能进行优化处理
2.0.12025-6-08对Solarflare网卡的ef_vi特性进行重构
2.1.02025-8-12对部分数据结构进行更新和优化
3.0.12026-5-08升级整个硬件系统的通讯组件,进一步降低通信延迟
4.0.12026-6-15对SDK部分的依赖进行优化,减少客户端对外部组件的依赖
Last Updated:
Contributors: chenglong