语言无关的金融市场数据交换协议
QA Protocol 是一套标准化的金融市场数据接口协议,专为量化交易系统设计。该协议解耦于编程语言,可无缝转换为 C++、Python、Rust 等主流语言的数据结构,并原生支持 Apache Arrow/Parquet 列式存储格式。
- ✅ QIFI/DIFF 协议兼容: 与中国量化交易生态主流协议 100% 兼容
- ✅ 多语言绑定生成: 自动生成 Python、Rust、C++、TypeScript 代码绑定
- ✅ 零开销设计: 数据结构与Arrow Schema分离,按需加载无性能损耗
- ✅ Arrow Schema 静态定义: 每种语言提供预定义 Arrow Schema 常量
- ✅ Arrow/Parquet 原生: 列式存储优化,高效查询与压缩
- ✅ 完整数据模型: 覆盖行情、账户、持仓、订单、成交、信号等 11 种数据类型
- ✅ 零拷贝序列化: 支持 rkyv 零拷贝,性能提升 25 倍
- ✅ 冲突已解决: 明确处理 CTP、QIFI、qaexchange-rs 间的数据模型差异
在中国量化交易领域,主流的数据接口包括:
- CTP API: 期货交易所官方接口(C++ 原生)
- QIFI 协议: Quantaxis 生态的标准化接口
- DIFF 协议: 快期科技的金融数据流协议
这些接口在数据建模、命名规范、消息格式上存在差异,导致系统集成时需要大量适配工作。
QA Protocol 通过分析以下开源项目的实现:
- qaexchange-rs: DIFF 协议的 Rust 实现,含 K-Line 聚合系统
- qautlra-rs: 市场数据结构与 Actor 并发模型
- open-trade-gateway: CTP 到 DIFF 的转换网关
- QIFI: Quantaxis 的账户/持仓/订单标准格式
提炼出一套统一的数据模型,在保持 QIFI 核心兼容的基础上,解决了以下关键冲突:
问题: 不同项目对 K线周期使用不同的 ID 编号方式
解决方案: 采用 qaexchange-rs 的 HQChart 格式(ID: 0, 3-8),保证与现有系统兼容
详见: docs/DESIGN.md Section 1
问题: CTP 返回双记录(多/空分离),QIFI 使用单记录(多空合并)
解决方案: 采用 QIFI 单记录模型,提供 CTP 双记录合并算法
详见: docs/DESIGN.md Section 2(含完整 Python 转换代码)
问题: QIFI 对同一概念使用多种命名(如 volume_long_his / volume_long_yd / pos_long_his)
解决方案: 这是 QIFI 有意设计,用于兼容不同生态。本协议全部保留,并说明推荐用法
详见: docs/DESIGN.md Section 3
问题: 现有项目未提供标准 Arrow schema 定义
解决方案: 基于源代码分析,推导出完整的 Arrow JSON schema(11 种数据类型)
位置: schemas/*.json
- Tick: 实时逐笔行情(8 字段)
- Snapshot: Level 2 市场快照,10 档深度(35+ 字段)
- K-Line: OHLC 蜡烛图,7 种周期(13 字段)
- Account: 账户资金与风险(19 字段)
- Position: 持仓多空分离(32 字段)
- Order: 订单生命周期(17 字段)
- Trade: 成交记录(13 字段)
- Transfer: 出入金流水(10 字段)
- Signal: 策略信号(11 字段)
- Event: 系统事件日志(7 字段)
- Notification: 用户通知(8 字段)
# 生成所有语言(Python, Rust, C++, TypeScript)
./scripts/generate_bindings.sh
# 只生成特定语言
./scripts/generate_bindings.sh --lang python
./scripts/generate_bindings.sh --lang rust,cpp
# 指定输出目录
./scripts/generate_bindings.sh --output ./my_bindings生成的代码位于 bindings/ 目录:
bindings/
├── python/ # Python bindings
│ ├── __init__.py # 重新导出 models
│ ├── models/ # 数据结构(无 Arrow 依赖)
│ │ ├── __init__.py
│ │ ├── tick.py
│ │ └── ... (11 个文件)
│ └── schemas/ # Arrow schemas(按需导入)
│ ├── __init__.py
│ ├── tick_schema.py
│ └── ... (11 个文件)
│
├── rust/ # Rust bindings
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── models/ # 数据结构(无 Arrow 依赖)
│ │ ├── mod.rs
│ │ ├── tick.rs
│ │ └── ... (11 个文件)
│ └── schemas/ # Arrow schemas(feature-gated)
│ ├── mod.rs
│ ├── tick_schema.rs
│ └── ... (11 个文件)
│
├── cpp/ # C++ bindings
│ └── include/qa_protocol/
│ ├── qa_protocol.h # 主头文件(只含 models)
│ ├── models/ # 数据结构(无 Arrow 依赖)
│ │ ├── models.h
│ │ ├── tick.h
│ │ └── ... (11 个文件)
│ └── schemas/ # Arrow schemas(按需 include)
│ ├── schemas.h
│ ├── tick_schema.h
│ └── ... (11 个文件)
│
└── typescript/ # TypeScript bindings
├── package.json
├── index.ts # 重新导出 models
├── models/ # 数据结构(无 Arrow 依赖)
│ ├── index.ts
│ ├── tick.ts
│ └── ... (11 个文件)
└── schemas/ # Arrow schemas(按需导入)
├── index.ts
├── tick_schema.ts
└── ... (11 个文件)
🎯 零开销设计 - models/ + schemas/ 分离架构:
- ✅ models/ - 数据结构,无 Arrow 依赖,零加载开销
- ✅ schemas/ - Arrow Schema 定义,按需导入
优势:
- 只用数据结构?→ 导入 models,无 Arrow 库加载,启动更快
- 需要列式存储?→ 按需导入 schemas,精确控制依赖
# 场景1: 只用数据结构(无Arrow加载,零开销)
from qa_protocol.models import Tick # ✅ 无pyarrow依赖
from datetime import datetime
tick = Tick(
instrument_id=b'SHFE.rb2501',
exchange_id=b'SHFE',
timestamp=datetime.now(),
trading_day=b'20250109',
last_price=3500.0,
volume=1000,
amount=3500000.0
)
# 场景2: 需要Arrow Schema时才导入(按需加载)
from qa_protocol.schemas.tick_schema import get_tick_schema, ticks_to_arrow
# 批量转换为 Arrow Table
ticks = [tick, ...] # 大量tick数据
table = ticks_to_arrow(ticks) # 使用预定义Schema
# 场景3: 写入 Parquet 文件
import pyarrow.parquet as pq
pq.write_table(table, "ticks.parquet", compression='snappy')
# 场景4: 用 Polars 读取 Parquet
import polars as pl
df = pl.scan_parquet("data/tick/**/*.parquet") \
.filter(pl.col("instrument_id") == b"SHFE.rb2501") \
.sort("timestamp", descending=True) \
.limit(100) \
.collect()// 场景1: 只用数据结构(无Arrow依赖,零开销)
use qa_protocol::models::Tick; // ✅ 默认无arrow依赖
let tick = Tick {
instrument_id: b"SHFE.rb2501".to_vec(),
exchange_id: b"SHFE".to_vec(),
timestamp: 1704729600000000000,
trading_day: b"20250109".to_vec(),
last_price: 3500.0,
volume: 1000,
amount: 3500000.0,
open_interest: Some(50000),
};
// 场景2: 需要Arrow Schema时启用feature(按需加载)
// Cargo.toml: qa_protocol = { version = "1.0", features = ["arrow"] }
#[cfg(feature = "arrow")]
{
use qa_protocol::schemas::tick_schema::TICK_SCHEMA;
use arrow::array::RecordBatch;
let schema = &*TICK_SCHEMA; // 引用静态schema
println!("Fields: {:?}", schema.fields());
// 使用TICK_SCHEMA构建列式数据
let batch = RecordBatch::try_new(schema.clone(), vec![/* columns */])?;
}// 场景1: 只用数据结构(无Arrow导入,零开销)
import { Tick } from 'qa-protocol'; // ✅ 无apache-arrow依赖
const tick: Tick = {
instrumentId: new Uint8Array([0x53, 0x48, 0x46, 0x45]),
exchangeId: new Uint8Array([0x53, 0x48, 0x46, 0x45]),
timestamp: 1704729600000000000,
tradingDay: new Uint8Array([0x32, 0x30, 0x32, 0x35]),
lastPrice: 3500.0,
volume: 1000,
amount: 3500000.0,
};
// 场景2: 需要Arrow Schema时才导入(按需加载)
import { TICK_SCHEMA } from 'qa-protocol/schemas/tick_schema';
import { tableFromArrays } from 'apache-arrow';
console.log('Schema fields:', TICK_SCHEMA.fields);
// 场景3: 创建 Arrow Table(列式存储)
const table = tableFromArrays({
instrumentId: [tick.instrumentId],
exchangeId: [tick.exchangeId],
// ... 其他字段
}, TICK_SCHEMA);完整示例: 参见 docs/IMPLEMENTATION.md
- 阅读本文档了解项目背景
- 查看
docs/SPECIFICATION.md理解数据结构 - 阅读
docs/IMPLEMENTATION.md学习代码实现
| 我想... | 查看文档 |
|---|---|
| 了解 Position 有哪些字段 | docs/SPECIFICATION.md → Section 4.2 |
| 理解为什么用 HQChart period | docs/DESIGN.md → Section 1 |
| 学习 Python 读写 Parquet | docs/IMPLEMENTATION.md → Section 3 |
| 转换 CTP 双记录为 QIFI | docs/DESIGN.md → Section 2(含算法) |
| 查看 Arrow schema 定义 | schemas/*.json + schemas/index.json |
- docs/SPECIFICATION.md - 完整技术规范(数据结构、协议细节)
- docs/DESIGN.md - 设计决策与冲突解决方案
- docs/IMPLEMENTATION.md - 实现指南(Rust/Python/C++ 代码示例)
- docs/FIELD_REQUIREMENTS.md - 字段要求规范(必填/可选说明)
- schemas/ - Arrow Schema 定义文件(11 个 JSON 文件)
qaprotocol/
├── README.md # 本文档(项目入口)
├── CLAUDE.md # 开发约束与贡献指南
├── VERSION # 📌 项目版本号(单一真相源)
├── CHANGELOG.md # 版本变更历史
├── .gitignore
│
├── docs/ # 📚 完整技术文档
│ ├── README.md # 文档导航中心
│ ├── SPECIFICATION.md # 协议技术规范
│ ├── DESIGN.md # 设计决策与冲突解决
│ ├── IMPLEMENTATION.md # 实现指南(代码示例)
│ └── FIELD_REQUIREMENTS.md # 字段要求规范
│
├── schemas/ # 📊 Arrow Schema 定义
│ ├── index.json # Schema 元数据目录(引用 VERSION)
│ ├── tick.json # Tick 数据 schema
│ ├── kline.json # K-Line schema
│ ├── account.json # 账户 schema
│ ├── position.json # 持仓 schema
│ └── ... (共 11 个数据类型)
│
├── scripts/ # 🔧 自动化脚本
│ ├── generate_bindings.sh # 主生成脚本(调用所有语言生成器)
│ ├── generate_python.py # Python 绑定生成器
│ ├── generate_rust.py # Rust 绑定生成器
│ ├── generate_cpp.py # C++ 绑定生成器
│ ├── generate_typescript.py # TypeScript 绑定生成器
│ └── update_version.sh # 版本号自动更新脚本
│
└── bindings/ # 🎯 生成的语言绑定(自动生成)
├── python/ # Python dataclass (11 模块)
├── rust/ # Rust struct + Cargo.toml
├── cpp/ # C++ headers (11 文件)
└── typescript/ # TypeScript interfaces + package.json
基于 qaexchange-rs 项目的实测数据:
| 操作 | 延迟 | 吞吐量 |
|---|---|---|
| JSON 序列化 | ~500 ns | 2M ops/s |
| rkyv 零拷贝 | ~20 ns | 50M ops/s ⚡ |
| Arrow 零拷贝 | ~50 ns | 20M ops/s |
| Parquet 写入 (Snappy) | - | 1.2 GB/s |
| Parquet 读取 | - | 1.5 GB/s |
- 数据格式: Apache Arrow, Apache Parquet
- 序列化: JSON (DIFF 协议), rkyv (零拷贝), Protobuf (QIFI)
- 语言绑定:
- Python: dataclass + typing
- Rust: struct + serde (可选)
- C++: struct + std::optional (C++17)
- TypeScript: interface + optional properties
- 代码生成: 基于 Arrow Schema JSON 自动生成
- 压缩: Snappy (实时), Zstd (批处理)
本项目遵循严格的代码复用原则,详见 CLAUDE.md:
- ✅ 优先扩展现有结构,而非创建新文件
- ✅ 所有设计决策需引用源项目代码
- ✅ 新增字段需明确标注为"协议扩展"
发现文档错误或有改进建议?请在 GitHub Issues 提交反馈。
本协议基于以下开源项目的分析与整合:
| 项目 | 贡献内容 | 源码位置 |
|---|---|---|
| qaexchange-rs | K-Line 聚合、DIFF 协议、Arrow 存储 | /solars_notes/top_project/qaexchange-rs/ |
| qautlra-rs | 市场数据结构、Actor 模型 | /solars_notes/top_project/qautlra-rs/ |
| open-trade-gateway | CTP 转 DIFF、WebSocket 网关 | /solars_notes/top_project/open-trade-gateway-master/ |
| QIFI | 账户/持仓/订单标准格式 | /solars_notes/base_project/QIFI-master/ |
QA Protocol 提供自动化代码生成器,从 Arrow Schema 定义生成多语言类型绑定。
| 语言 | 类型系统 | 可选字段处理 | 输出格式 |
|---|---|---|---|
| Python | dataclass | Optional[T] |
.py 模块 |
| Rust | struct | Option<T> |
.rs 模块 + Cargo.toml |
| C++ | struct | std::optional<T> |
.h 头文件 (C++17) |
| TypeScript | interface | field?: |
.ts 接口 + package.json |
# 生成所有语言到默认目录 (bindings/)
./scripts/generate_bindings.sh
# 只生成特定语言
./scripts/generate_bindings.sh --lang python # 只生成 Python
./scripts/generate_bindings.sh --lang rust,cpp # 生成 Rust 和 C++
# 指定自定义输出目录
./scripts/generate_bindings.sh --output ./my_output
# 组合使用
./scripts/generate_bindings.sh --lang typescript --output ~/my_project/typesPython (bindings/python/)
python/
├── __init__.py # 重新导出 models
├── models/ # 数据结构(无 Arrow 依赖)
│ ├── __init__.py
│ ├── tick.py
│ ├── account.py
│ └── ... (共 11 个模块)
└── schemas/ # Arrow schemas(按需导入)
├── __init__.py
├── tick_schema.py
├── account_schema.py
└── ... (共 11 个模块)
Rust (bindings/rust/)
rust/
├── Cargo.toml # 包配置(含 arrow feature)
└── src/
├── lib.rs # 导出 models 和 schemas
├── models/ # 数据结构(无 Arrow 依赖)
│ ├── mod.rs
│ ├── tick.rs
│ └── ... (共 11 个模块)
└── schemas/ # Arrow schemas(feature-gated)
├── mod.rs
├── tick_schema.rs
└── ... (共 11 个模块)
C++ (bindings/cpp/)
cpp/
└── include/qa_protocol/
├── qa_protocol.h # 主头文件(只含 models)
├── models/ # 数据结构(无 Arrow 依赖)
│ ├── models.h
│ ├── tick.h
│ └── ... (共 11 个头文件)
└── schemas/ # Arrow schemas(按需 include)
├── schemas.h
├── tick_schema.h
└── ... (共 11 个头文件)
TypeScript (bindings/typescript/)
typescript/
├── package.json # npm 包配置
├── tsconfig.json # TypeScript 配置
├── index.ts # 重新导出 models
├── models/ # 数据结构(无 Arrow 依赖)
│ ├── index.ts
│ ├── tick.ts
│ └── ... (共 11 个文件)
└── schemas/ # Arrow schemas(按需导入)
├── index.ts
├── tick_schema.ts
└── ... (共 11 个文件)
| Arrow 类型 | Python | Rust | C++ | TypeScript |
|---|---|---|---|---|
| binary | bytes |
Vec<u8> |
std::vector<uint8_t> |
Uint8Array |
| utf8 | str |
String |
std::string |
string |
| int/long | int |
i32/i64 |
int32_t/int64_t |
number |
| double | float |
f64 |
double |
number |
| bool | bool |
bool |
bool |
boolean |
| timestamp | datetime |
i64 (ns) |
int64_t (ns) |
number (ns) |
| struct | 嵌套 dataclass | 嵌套 struct | 嵌套 struct | 嵌套 interface |
当 schemas/*.json 文件更新后,重新生成绑定:
# 1. 修改 schema 文件
vim schemas/tick.json
# 2. 更新版本号(如果是不兼容变更)
./scripts/update_version.sh 2.0.0
# 3. 重新生成所有语言绑定
./scripts/generate_bindings.sh
# 4. 提交变更
git add schemas/ VERSION
git commit -m "feat: add new field to Tick schema"QA Protocol 采用 语义化版本 2.0.0 规范:
主版本号.次版本号.修订号 (MAJOR.MINOR.PATCH)
例如: 1.0.0
递增规则:
- 主版本号(MAJOR):不兼容的 API 修改(如字段删除、类型变更)
- 次版本号(MINOR):向下兼容的功能新增(如新增 schema、新增可选字段)
- 修订号(PATCH):向下兼容的问题修正(如文档修正、性能优化)
📌 单一真相源(Single Source of Truth):
QA Protocol 所有 schema 使用 统一的项目版本号,版本号定义在根目录的 VERSION 文件中。
VERSION 文件 (1.0.0)
↓ 被引用
schemas/index.json
↓ 被引用
各个 schema 文件(不包含独立 version 字段)
为什么采用统一版本?
- ✅ schemas 作为整体发布(Account/Position/Order 高度耦合)
- ✅ 简化版本管理,避免兼容性矩阵爆炸
- ✅ 单一真相源,避免版本号不一致
- ✅ 版本更新只需修改 1 个文件
1. 自动更新(推荐)
# 更新到新版本(如 1.1.0)
./scripts/update_version.sh 1.1.0脚本会自动更新:
VERSION文件schemas/index.jsonREADME.md版本徽章
2. 手动更新
然后手动更新以下文件:
CHANGELOG.md- 添加新版本变更记录README.md- 更新版本历史表
| 版本 | 日期 | 变更说明 |
|---|---|---|
| 1.0.0 | 2025-01-08 | 初始版本发布,包含 11 种数据类型完整规范 |
详细变更记录请查看 CHANGELOG.md
本项目采用 MIT 许可证。详见 LICENSE 文件。
感谢 Quantaxis、快期科技、qaexchange-rs 等开源社区的贡献。本协议旨在推动中国量化交易生态的标准化进程。
维护团队: QA Protocol Team 最后更新: 2025-01-08 联系方式: 参见 GitHub Issues