作者: @yutiansut @quantaxis 版本: v2.0.0 最后更新: 2025-10-24
基于CTP API的高性能期货行情数据WebSocket服务器,提供实时行情数据分发服务。该项目是QuantAxis交易网关系统的独立行情模块,专门负责期货市场数据的接入、处理和分发。
核心定位: 作为 QuantAxis QIFI 体系的市场数据层组件,与 MongoDB QIFI 数据结构无缝集成,为策略层提供实时、高性能的行情数据流。
- 🔥 多CTP连接管理: 支持同时连接90+期货公司,覆盖全国主要期货交易商
- ⚡ 智能负载均衡: 4种负载均衡策略,支持25000+合约并发订阅
- 🛡️ 故障自动转移: 连接断开时自动迁移订阅到其他可用连接
- 📊 海量订阅支持: 单一系统支持数万个合约的实时行情订阅
- 🚀 高性能架构: 异步I/O + 连接池 + 智能分发,毫秒级延迟
- 📡 WebSocket服务: 基于Boost.Beast的高性能WebSocket服务器
- 🧠 智能订阅管理: 增量订阅机制,避免重复CTP订阅,提高系统效率
- 👥 多客户端支持: 支持多个WebSocket客户端同时连接,精准推送
- 💾 Redis数据缓存: 集成Redis存储,提供行情数据持久化
- 🔧 灵活配置管理: JSON配置文件,支持动态添加期货公司连接
- 💽 独立共享内存: 使用专用共享内存段
qamddata,与主项目解耦 - 🔗 数据结构兼容: 与QuantAxis QIFI数据结构完全兼容
- 📝 MongoDB同步: 支持与QIFI MongoDB数据库同步,保持数据一致性
- 🎯 策略层支持: 为Python/C++策略层提供实时市场数据接口
┌─────────────────────────────────────────────────────────────────────────────┐
│ 多CTP连接管理系统 │
│ (90+期货公司连接池) │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────┼─────────────────────────────┐
│ │ │
┌───────▼─────┐ ┌────────▼─────┐ ┌────────▼─────┐
│ 广发期货 │ │ 国泰君安 │ │ 中信期货 │
│ (9000) │ │ (7090) │ │ (66666) │
│ 600订阅/连接│ │ 600订阅/连接 │ │ 600订阅/连接 │
└─────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
┌─────▼─────┐ ┌────────▼─────┐ ┌────────▼─────┐
│ 海通期货 │ │ 华泰期货 │ │ 银河期货 │
│ (8000) │ │ (8080) │ │ (4040) │
│ 400订阅/连接│ │ 600订阅/连接 │ │ 350订阅/连接 │
└─────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└─────────────┬─────────────────────────────┬─────────────┘
│ │
┌───────▼──────┐ ┌───────▼──────┐
│ 订阅分发器 │ │ 负载均衡器 │
│ 25000+合约 │ │ 4种策略 │
└───────┬──────┘ └──────┬───────┘
│ │
└─────────┬──────────────────┘
│
┌─────────▼─────────┐
│ MarketDataServer│
│ 核心协调层 │
└─────────┬─────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌───────▼───────┐ ┌──▼───────┐ ┌──▼──────────┐
│ WebSocket │ │ Redis │ │ 共享内存 │
│ 服务器 │ │ 缓存层 │ │ (qamddata) │
│ (7799) │ │ │ │ │
└───────┬───────┘ └──┬───────┘ └──┬──────────┘
│ │ │
┌───────▼────────────▼────────────▼───────┐
│ 数据分发与存储层 │
│ │
│ • WebSocket实时推送 (JSON格式) │
│ • Redis最新行情 (String) │
│ • Redis历史Tick (ZSet时间序列) │
│ • 共享内存快速访问 │
└──────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌───────▼───────┐ ┌──▼───────┐ ┌──▼──────────┐
│ WebSocket │ │ Python │ │ C++ │
│ 客户端 │ │ 策略层 │ │ 策略层 │
└───────────────┘ └──────────┘ └─────────────┘
- 职责: 管理90+期货公司的CTP连接池
- 关键功能:
- 连接池生命周期管理
- 连接质量监控和评估
- 故障检测和自动重连
- 连接负载均衡调度
- 职责: 智能分发订阅请求到最优CTP连接
- 核心算法:
- 轮询分发 (Round Robin)
- 最少连接优先 (Least Connections)
- 连接质量优先 (Connection Quality)
- 哈希分发 (Hash Based)
- 职责: 协调多CTP连接、WebSocket服务、订阅管理
- 关键功能:
- 多CTP连接协调管理
- WebSocket会话管理
- 智能订阅策略实现
- Redis缓存管理
- 共享内存管理
- 职责: 管理单个期货公司的CTP连接
- 关键回调:
OnFrontConnected(): CTP前置机连接成功OnRspUserLogin(): CTP登录响应OnRtnDepthMarketData(): 实时行情数据推送OnRspSubMarketData(): 行情订阅响应
- 职责: 管理单个WebSocket连接的生命周期
- 功能:
- 消息解析和路由
- 连接状态管理
- 订阅列表维护
- 异步消息发送
- 职责: 行情数据的持久化存储和时间序列管理
- 核心功能:
- 实时数据存储: 每个合约最新行情数据实时更新到Redis (key格式:
instrument_id) - 历史数据存储: 使用ZSet存储历史tick数据 (key格式:
history:instrument_id) - 时间序列索引: 使用timestamp_ms作为ZSet的score,支持时间范围查询
- 自动过期清理: 历史数据超过10万条时,自动清理2天前的旧数据
- 连接池管理: 线程安全的Redis连接管理,支持自动重连
- 实时数据存储: 每个合约最新行情数据实时更新到Redis (key格式:
- 数据格式: JSON格式存储,与WebSocket推送格式完全一致
- 支持操作:
- String:
SET/GET/EXISTS/DEL - Hash:
HSET/HGET/HGETALL - ZSet:
ZADD/ZCARD/ZREMRANGEBYSCORE(用于时间序列)
- String:
- C++17: 现代C++特性,智能指针、移动语义
- Boost.Asio: 异步网络I/O框架
- Boost.Beast: HTTP/WebSocket协议实现
- Boost.Interprocess: 跨进程共享内存
- RapidJSON: 高性能JSON解析库
- CTP API: 上期技术期货交易API
- Hiredis: Redis C客户端库,支持连接池和线程安全
- 项目结构规划
- 依赖管理和编译系统(Makefile)
- 核心类设计(MarketDataServer, WebSocketSession, MarketDataSpi)
- CTP API集成框架
- CTP前置机连接管理
- 登录认证流程(无需用户名密码)
- 连接状态监控和重连机制
- 行情订阅/取消订阅API封装
- WebSocket服务器搭建
- 连接会话管理
- JSON消息协议设计
- 消息路由和处理机制
- 增量订阅算法实现
- 会话-合约订阅映射管理
- 会话断开自动清理
- CTP订阅优化(避免重复订阅)
- 行情数据结构转换
- JSON格式化输出
- 精准推送(只推送给订阅客户端)
- 异步消息队列管理
- 独立共享内存段创建(
qamddata) - 与主项目数据结构兼容性
- 内存管理和清理机制
- 线程安全保障
- 错误处理和恢复机制
- 日志系统完善
- 内存泄漏防护
- 命令行参数解析
- 配置管理
- 系统集成脚本
- 故障诊断工具
- 操作系统: Linux (推荐Ubuntu 18.04+)
- 编译器: GCC 7.0+ (支持C++17)
- 内存: 至少512MB可用内存
- 网络: 能访问CTP前置机的网络环境
# Ubuntu/Debian系统
sudo apt-get update
sudo apt-get install -y \
build-essential \
libboost-all-dev \
libssl-dev \
libcurl4-openssl-dev \
rapidjson-dev \
libhiredis-dev \
redis-server
# 启动Redis服务
sudo systemctl start redis-server
sudo systemctl enable redis-server
# 验证Redis安装
redis-cli ping # 应返回 PONG# 1. 进入项目目录
cd qactpmdgateway
# 2. 检查系统依赖
make check-deps
# 3. 编译项目
make all
# 4. 验证编译结果
ls -la bin/market_data_server
# 5. 安装到系统(可选)
sudo make install# 使用默认多CTP配置 (90个期货公司)
./bin/market_data_server --multi-ctp
# 使用自定义配置文件
./bin/market_data_server --config config/multi_ctp_config.json
# 指定负载均衡策略
./bin/market_data_server --multi-ctp --strategy round_robin
# 检查连接状态
./bin/market_data_server --multi-ctp --status
# 后台运行多CTP模式
nohup ./bin/market_data_server --multi-ctp > logs/server.log 2>&1 &# 使用SimNow模拟环境(默认配置)
./bin/market_data_server
# 使用自定义单CTP配置
./bin/market_data_server \
--front-addr tcp://182.254.243.31:30011 \
--broker-id 9999 \
--port 7799{
"websocket_port": 7799,
"redis_host": "192.168.2.27", // Redis服务器地址
"redis_port": 6379, // Redis端口
"load_balance_strategy": "connection_quality",
"health_check_interval": 30,
"maintenance_interval": 60,
"max_retry_count": 3,
"auto_failover": true,
"connections": [
{
"connection_id": "guangfa_telecom",
"front_addr": "tcp://101.230.102.231:51213",
"broker_id": "9000",
"max_subscriptions": 600,
"priority": 1,
"enabled": true
}
]
}- 策略: 根据连接质量评分选择最优连接
- 评分因素: 延迟、错误率、连接稳定性
- 适用场景: 追求最优行情质量的生产环境
- 策略: 按顺序轮流分配订阅到各个连接
- 特点: 负载分布均匀,简单可靠
- 适用场景: 连接质量相近的环境
- 策略: 优先选择当前订阅数量最少的连接
- 特点: 动态负载均衡,避免单点过载
- 适用场景: 订阅数量动态变化的场景
- 策略: 根据合约ID哈希值分配到固定连接
- 特点: 相同合约总是路由到相同连接
- 适用场景: 需要数据一致性的场景
| 期货公司 | Broker ID | 订阅容量 | 网络 | 状态 |
|---|---|---|---|---|
| 上期技术 SimNow | 9999 | 500 | 电信 | ✅ |
| 广发期货 | 9000 | 600 | 电信/联通 | ✅ |
| 国泰君安期货 | 7090 | 600 | 电信/联通 | ✅ |
| 华泰期货 | 8080 | 600 | 电信/网通 | ✅ |
| 海通期货 | 8000 | 400 | 电信/联通 | ✅ |
| 中信期货 | 66666 | 600 | 电信/联通 | ✅ |
| 银河期货 | 4040 | 350 | 电信/联通 | ✅ |
| 一德期货 | 5060 | 800 | 电信/联通 | ✅ |
| 方正中期期货 | 0034 | 400 | 电信/联通 | ✅ |
| 光大期货 | 6000 | 400 | 电信/联通 | ✅ |
| ... | ... | ... | ... | ... |
| 总计 | 90家 | 约25000 | 全网覆盖 | ✅ |
# 验证配置文件
./bin/market_data_server --config config/multi_ctp_config.json --status
# 测试特定期货公司连接
./bin/market_data_server --config config/test_single_broker.json
# 使用broker解析工具生成配置
python3 broker_parser.py # 解析broker.xml生成broker_data.json项目提供了broker_parser.py工具,用于从标准的broker.xml文件自动生成配置:
# 解析broker.xml文件
python3 broker_parser.py
# 输出格式化的broker信息
# 自动生成broker_data.json文件
# 包含90+期货公司的完整连接信息broker_parser.py功能特点:
- 📋 解析标准broker.xml格式文件
- 🔄 自动提取BrokerID和MarketData地址
- 📊 生成结构化JSON配置数据
- 📈 统计各期货公司的连接数量
- 🌏 支持中文broker名称显示
使用示例:
正在解析broker.xml文件...
找到 90 个broker
统计信息:
BrokerID: 9999, 名称: 上期技术, MarketData地址数量: 3
BrokerID: 9000, 名称: 广发期货, MarketData地址数量: 2
BrokerID: 7090, 名称: 国泰君安期货, MarketData地址数量: 2
...
结果已保存到: broker_data.jsonws://hostname:7799/
系统支持两种WebSocket协议格式,完全兼容:
请求格式:
{
"aid": "subscribe_quote",
"ins_list": "rb2501,i2501,au2512"
}响应格式:
{
"aid": "subscribe_quote",
"status": "ok"
}说明:
ins_list: 逗号分隔的合约列表- 支持交易所前缀(如
SHFE.rb2501),系统会自动去除前缀 - 兼容QuantAxis mdservice协议
请求格式:
{
"aid": "peek_message"
}响应格式(完整数据):
{
"aid": "rtn_data",
"data": [
{
"quotes": {
"rb2501": {
"instrument_id": "rb2501",
"datetime": "2025-10-24 14:30:15.500",
"last_price": 4180.0,
"volume": 12580,
"ask_price1": 4180.0,
"ask_volume1": 8,
"bid_price1": 4179.0,
"bid_volume1": 10,
"open_interest": 156890.0,
"...": "..."
},
"i2501": {
"...": "..."
}
}
},
{
"account_id": "",
"ins_list": "",
"mdhis_more_data": false
}
]
}响应格式(增量数据):
{
"aid": "rtn_data",
"data": [
{
"quotes": {
"rb2501": {
"last_price": 4181.0,
"volume": 12590
}
}
},
{
"account_id": "",
"ins_list": "",
"mdhis_more_data": false
}
]
}说明:
peek_message实现长轮询机制- 首次请求返回所有订阅合约的完整数据
- 后续请求只返回发生变化的字段(diff机制)
- 如果没有数据变化,服务器会挂起请求,直到有新数据才返回
- 实现了高效的增量推送机制
请求格式:
{
"action": "subscribe",
"instruments": ["rb2501", "i2501", "au2412"]
}响应格式:
{
"type": "subscribe_response",
"status": "success",
"subscribed_count": 3
}请求格式:
{
"action": "unsubscribe",
"instruments": ["rb2501"]
}响应格式:
{
"type": "unsubscribe_response",
"status": "success",
"subscribed_count": 2
}请求格式:
{
"action": "list_instruments"
}响应格式:
{
"type": "instrument_list",
"instruments": ["rb2501", "rb2502", "i2501", "..."],
"total_count": 1250
}请求格式:
{
"action": "search",
"pattern": "rb"
}响应格式:
{
"type": "search_result",
"pattern": "rb",
"instruments": ["rb2501", "rb2502", "rb2503"],
"match_count": 3
}推送格式:
{
"type": "market_data",
"instrument_id": "rb2501",
"trading_day": "20231201",
"update_time": "09:30:15",
"update_millisec": 500,
"last_price": 4180.0,
"pre_settlement_price": 4170.0,
"pre_close_price": 4175.0,
"pre_open_interest": 155000.0,
"open_price": 4175.0,
"highest_price": 4185.0,
"lowest_price": 4172.0,
"volume": 12580,
"turnover": 525490000.0,
"open_interest": 156890.0,
"close_price": 4178.0,
"settlement_price": 4179.0,
"upper_limit_price": 4587.0,
"lower_limit_price": 3753.0,
"pre_delta": 0.0,
"curr_delta": 0.0,
"bid_price1": 4179.0,
"bid_volume1": 10,
"ask_price1": 4180.0,
"ask_volume1": 8,
"bid_price2": 4178.0,
"bid_volume2": 15,
"ask_price2": 4181.0,
"ask_volume2": 12,
"average_price": 4177.5,
"action_day": "20231201",
"timestamp": 1701398415500
}| 协议类型 | 推荐场景 | 优势 |
|---|---|---|
| aid协议 | QuantAxis策略、QIFI体系集成 | 增量推送、长轮询、兼容mdservice |
| action协议 | 第三方客户端、实时推送场景 | 简单直观、主动推送 |
import asyncio
import websockets
import json
async def aid_protocol_client():
"""使用aid协议的长轮询客户端"""
uri = "ws://localhost:7799"
async with websockets.connect(uri) as websocket:
# 1. 订阅合约
subscribe_msg = {
"aid": "subscribe_quote",
"ins_list": "rb2501,i2501,au2512"
}
await websocket.send(json.dumps(subscribe_msg))
response = await websocket.recv()
print(f"订阅响应: {response}")
# 2. 长轮询获取行情数据
while True:
peek_msg = {"aid": "peek_message"}
await websocket.send(json.dumps(peek_msg))
# 服务器会在有数据变化时才返回(长轮询)
response = await websocket.recv()
data = json.loads(response)
if data.get("aid") == "rtn_data":
quotes = data["data"][0]["quotes"]
for instrument_id, quote in quotes.items():
print(f"{instrument_id}: {quote.get('last_price', 'N/A')}")
# 运行
asyncio.run(aid_protocol_client())import asyncio
import websockets
import json
async def action_protocol_client():
"""使用action协议的主动推送客户端"""
uri = "ws://localhost:7799"
async with websockets.connect(uri) as websocket:
# 1. 订阅合约
subscribe_msg = {
"action": "subscribe",
"instruments": ["rb2501", "i2501", "au2512"]
}
await websocket.send(json.dumps(subscribe_msg))
# 2. 接收服务器主动推送的行情数据
async for message in websocket:
data = json.loads(message)
if data.get("type") == "market_data":
print(f"{data['instrument_id']}: {data['last_price']}")
elif data.get("type") == "subscribe_response":
print(f"订阅成功: {data['subscribed_count']} 个合约")
# 运行
asyncio.run(action_protocol_client())系统将行情数据实时存储到Redis,提供两种数据访问模式:
# 数据结构
Key: {instrument_id} # 例如: "rb2601"
Value: {JSON格式的完整行情数据}
TTL: 永久存储 (每次更新覆盖)
# Redis查询示例
redis-cli get rb2601
redis-cli get i2501
redis-cli exists au2512# 数据结构
Key: history:{instrument_id} # 例如: "history:rb2601"
Score: timestamp_ms # 毫秒级时间戳作为排序依据
Member: {JSON格式的完整行情数据}
自动清理: 超过10万条记录时,删除2天前的数据
# Redis查询示例 - 按时间范围查询
# 查询最近100条tick
redis-cli ZREVRANGE history:rb2601 0 99 WITHSCORES
# 按时间范围查询 (timestamp_ms)
redis-cli ZRANGEBYSCORE history:rb2601 1701398400000 1701484800000
# 统计历史数据量
redis-cli ZCARD history:rb2601import redis
import json
from datetime import datetime, timedelta
# 连接Redis
r = redis.Redis(host='192.168.2.27', port=6379, decode_responses=True)
# 1. 获取最新行情
def get_latest_quote(instrument_id):
data = r.get(instrument_id)
if data:
return json.loads(data)
return None
# 2. 获取历史Tick数据
def get_historical_ticks(instrument_id, start_time=None, end_time=None, limit=100):
"""
获取历史tick数据
start_time/end_time: datetime对象或None
limit: 最多返回多少条记录
"""
key = f"history:{instrument_id}"
if start_time and end_time:
# 转换为毫秒时间戳
start_ms = int(start_time.timestamp() * 1000)
end_ms = int(end_time.timestamp() * 1000)
# 按时间范围查询
results = r.zrangebyscore(key, start_ms, end_ms, withscores=True)
else:
# 获取最新N条
results = r.zrevrange(key, 0, limit-1, withscores=True)
ticks = []
for data, timestamp_ms in results:
tick = json.loads(data)
tick['redis_timestamp'] = timestamp_ms
ticks.append(tick)
return ticks
# 3. 批量获取多个合约的最新行情
def get_multiple_quotes(instrument_ids):
"""批量获取最新行情"""
pipe = r.pipeline()
for instrument_id in instrument_ids:
pipe.get(instrument_id)
results = pipe.execute()
quotes = {}
for i, data in enumerate(results):
if data:
quotes[instrument_ids[i]] = json.loads(data)
return quotes
# 4. 实时监控行情更新 (使用Redis PubSub - 需要额外配置)
def subscribe_market_data(instrument_ids):
"""订阅行情更新通知"""
pubsub = r.pubsub()
channels = [f"quote:{inst}" for inst in instrument_ids]
pubsub.subscribe(*channels)
for message in pubsub.listen():
if message['type'] == 'message':
print(f"收到行情: {message['channel']}")
data = json.loads(message['data'])
print(f" 价格: {data['last_price']}")
# 使用示例
if __name__ == "__main__":
# 获取rb2601最新行情
quote = get_latest_quote("rb2601")
if quote:
print(f"合约: {quote['instrument_id']}")
print(f"最新价: {quote['last_price']}")
print(f"时间: {quote['update_time']}")
# 获取最近1小时的历史数据
end_time = datetime.now()
start_time = end_time - timedelta(hours=1)
ticks = get_historical_ticks("rb2601", start_time, end_time)
print(f"获取到 {len(ticks)} 条历史tick数据")
# 批量获取多个合约
quotes = get_multiple_quotes(["rb2601", "i2501", "au2512"])
for inst_id, quote in quotes.items():
print(f"{inst_id}: {quote['last_price']}")# 监控Redis连接状态
redis-cli INFO clients
redis-cli INFO stats
# 查看所有合约keys
redis-cli KEYS "*" | grep -v "history:"
# 查看历史数据keys
redis-cli KEYS "history:*"
# 监控写入性能
redis-cli MONITOR | grep -E "(SET|ZADD)"
# 检查内存使用
redis-cli INFO memory
# 清理测试数据
redis-cli FLUSHDB # 谨慎使用!# /etc/redis/redis.conf 优化建议
# 1. 内存配置
maxmemory 4gb
maxmemory-policy allkeys-lru # 内存不足时使用LRU策略
# 2. 持久化配置 (根据需求选择)
# RDB方式 - 快照持久化
save 900 1
save 300 10
save 60 10000
# AOF方式 - 更安全但性能略低
appendonly yes
appendfsync everysec
# 3. 网络优化
tcp-backlog 511
timeout 300
tcp-keepalive 300
# 4. 性能优化
# 禁用慢查询
slowlog-log-slower-than 10000
slowlog-max-len 128
# 5. 安全配置
bind 192.168.2.27
requirepass your_password # 建议生产环境设置密码项目提供了多个Redis调试工具:
# 检查Redis连接和数据
python3 check_redis.py
# 调试Redis数据写入
python3 debug_redis.py
# 简单Redis测试
python3 simple_redis_test.py// 伪代码示例
void MarketDataServer::subscribe_instrument(session_id, instrument_id) {
subscribers[instrument_id].insert(session_id);
// 智能CTP订阅: 只有第一个客户端时才向CTP订阅
if (subscribers[instrument_id].size() == 1) {
ctp_api->SubscribeMarketData({instrument_id});
log_info("New CTP subscription: " + instrument_id);
}
}
void MarketDataServer::unsubscribe_instrument(session_id, instrument_id) {
subscribers[instrument_id].erase(session_id);
// 智能CTP取消订阅: 没有客户端时才从CTP取消订阅
if (subscribers[instrument_id].empty()) {
subscribers.erase(instrument_id);
ctp_api->UnSubscribeMarketData({instrument_id});
log_info("CTP subscription removed: " + instrument_id);
}
}- 网络优化: 减少不必要的CTP订阅请求
- 资源节约: 避免CTP端重复处理相同合约
- 系统稳定: 降低CTP API调用频率,提高稳定性
- 成本控制: 部分CTP接口可能按订阅量计费
# 开发调试版本
make debug
# 生产发布版本
make release
# 清理编译文件
make clean
# 生成文档
make docs
# 运行基础测试
make testqactpmdgateway/
├── src/ # 源代码目录
│ ├── main.cpp # 程序入口点
│ ├── market_data_server.h # 服务器类声明
│ └── market_data_server.cpp # 服务器类实现
├── libs/ # CTP库文件
│ ├── thostmduserapi_se.so
│ └── thosttraderapi_se.so
├── obj/ # 编译中间文件
├── bin/ # 可执行文件输出
├── ctp_flow/ # CTP日志文件
├── Makefile # 构建配置
└── README.md # 项目文档
服务器提供三级日志输出:
- INFO: 一般信息,如连接建立、订阅状态等
- WARNING: 警告信息,如重连尝试、数据异常等
- ERROR: 错误信息,如连接失败、解析错误等
ctpflow/ # CTP Flow文件统一目录
├── guangfa_telecom/ # 广发期货连接目录
│ ├── DialogRsp.con # 对话响应文件
│ ├── QueryRsp.con # 查询响应文件
│ └── TradingDay.con # 交易日文件
├── guotai_telecom/ # 国泰君安连接目录
├── zhongxin_telecom/ # 中信期货连接目录
├── yide_telecom/ # 一德期货连接目录
├── ... (90个期货公司目录)
└── single/ # 单CTP模式专用目录
├── DialogRsp.con
├── QueryRsp.con
└── TradingDay.con
# 查看所有CTP连接目录
ls -la ctpflow/
# 查看特定期货公司的日志文件
ls -la ctpflow/guangfa_telecom/
# 监控CTP连接状态
find ctpflow/ -name "*.con" -mmin -5 # 最近5分钟更新的文件
# 清理所有CTP Flow文件
rm -rf ctpflow/
# 备份CTP Flow文件
tar -czf ctpflow_backup_$(date +%Y%m%d_%H%M).tar.gz ctpflow/
# 统计连接数
ls -d ctpflow/*/ | wc -l# 实时监控特定连接的活动
watch -n 1 "ls -la ctpflow/guangfa_telecom/ | tail -5"
# 检查连接健康状态
for dir in ctpflow/*/; do
echo "$(basename "$dir"): $(ls "$dir" 2>/dev/null | wc -l) files"
done
# 查找最活跃的连接
find ctpflow/ -name "*.con" -mmin -1 -exec dirname {} \; | sort | uniq -c | sort -nr问题: fatal error: boost/asio.hpp: No such file or directory
# 解决方案
sudo apt-get install libboost-all-dev
# 或指定版本
sudo apt-get install libboost1.71-all-dev问题: CTP库链接错误
# 解决方案
# 确保CTP库文件存在
ls -la libs/thostmduserapi_se.so libs/thosttraderapi_se.so
# 设置库搜索路径
export LD_LIBRARY_PATH=./libs:$LD_LIBRARY_PATH问题: CTP连接失败
# 检查网络连通性
telnet 182.254.243.31 30011
# 检查防火墙设置
sudo ufw status
# 验证CTP前置机地址
ping 182.254.243.31问题: 共享内存权限错误
# 查看现有共享内存
ls -la /dev/shm/
# 清理旧的共享内存
sudo rm -f /dev/shm/qamddata
# 检查用户权限
id问题: 某些期货公司连接失败
# 检查特定连接的CTP流文件
ls -la ctpflow/guangfa_telecom/
# 查看连接状态
./bin/market_data_server --config config/multi_ctp_config.json --status
# 测试单个连接
grep -A 20 "guangfa_telecom" config/multi_ctp_config.json问题: 订阅分发不均匀
# 检查各连接订阅数量
tail -f server.log | grep "subscription count"
# 验证负载均衡策略
./bin/market_data_server --multi-ctp --strategy least_connections
# 监控连接质量
watch -n 5 'find ctpflow/ -name "*.con" -mmin -1 | wc -l'问题: 连接数过多导致系统资源不足
# 监控进程资源使用
top -p `pgrep market_data_server`
# 检查打开的文件句柄数
lsof -p `pgrep market_data_server` | wc -l
# 临时禁用部分连接
# 在配置文件中设置 "enabled": false问题: 客户端连接被拒绝
# 检查端口占用
netstat -tlnp | grep 7799
# 检查防火墙规则
sudo ufw allow 7799
# 测试本地连接
curl --include \
--no-buffer \
--header "Connection: Upgrade" \
--header "Upgrade: websocket" \
--header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
--header "Sec-WebSocket-Version: 13" \
http://localhost:7799/# 进程监控
top -p `pgrep market_data_server`
# 内存使用
pmap -x `pgrep market_data_server`
# 网络连接
ss -tlnp | grep market_data_server# CTP流文件位置
ls -la ctp_flow/
# 实时监控CTP日志
tail -f ctp_flow/*.log# 使用wscat测试
npm install -g wscat
wscat -c ws://localhost:7799
# 发送订阅消息
> {"action":"subscribe","instruments":["rb2501"]}
# 发送查询消息
> {"action":"list"}# 增加文件描述符限制
ulimit -n 65536
# 调整网络缓冲区
echo 'net.core.rmem_max = 134217728' >> /etc/sysctl.conf
echo 'net.core.wmem_max = 134217728' >> /etc/sysctl.conf
sysctl -p# 使用优化编译选项
make release
# 启用特定CPU优化
export CXXFLAGS="-O3 -march=native -mtune=native"
make clean && make all# 核心性能指标监控脚本
#!/bin/bash
echo "=== QuantAxis 多CTP系统监控报告 ==="
echo "时间: $(date)"
echo
# 1. CTP连接状态
echo "📊 CTP连接状态:"
active_connections=$(ls -d ctpflow/*/ 2>/dev/null | wc -l)
echo " 活跃连接数: $active_connections"
recent_activity=$(find ctpflow/ -name "*.con" -mmin -5 2>/dev/null | wc -l)
echo " 最近活跃: $recent_activity 个文件更新"
# 2. 系统资源使用
echo
echo "💻 系统资源使用:"
pid=$(pgrep market_data_server)
if [ ! -z "$pid" ]; then
cpu_usage=$(top -bn1 -p $pid | tail -1 | awk '{print $9}')
mem_usage=$(top -bn1 -p $pid | tail -1 | awk '{print $10}')
echo " CPU使用率: ${cpu_usage}%"
echo " 内存使用率: ${mem_usage}%"
# 文件句柄数
fd_count=$(lsof -p $pid 2>/dev/null | wc -l)
echo " 打开文件数: $fd_count"
fi
# 3. 网络连接
echo
echo "🌐 网络连接状态:"
ws_connections=$(ss -tn | grep :7799 | grep ESTABLISHED | wc -l)
echo " WebSocket连接: $ws_connections"
ctp_connections=$(ss -tn | grep -E ':(10210|10211|51213|61213|41313)' | grep ESTABLISHED | wc -l)
echo " CTP连接数: $ctp_connections"
# 4. 订阅统计
echo
echo "📈 订阅统计:"
if [ -f server.log ]; then
total_subs=$(tail -100 server.log | grep -o 'Total subscriptions: [0-9]*' | tail -1 | awk '{print $3}')
echo " 总订阅数: ${total_subs:-0}"
fi
echo
echo "=== 监控完成 ==="基础指标:
- WebSocket连接数量
- 行情订阅合约数量
- 内存使用量
- CPU使用率
- 网络I/O吞吐量
多CTP特有指标:
- 🔗 活跃CTP连接数 (目标: 90个)
- ⚡ 连接质量评分 (延迟、错误率)
- 📊 订阅分发均匀度
- 🔄 故障转移响应时间
- 📁 CTP Flow文件更新频率
- 🎯 负载均衡效率
本项目严格遵循主项目的数据结构定义:
// 引用主项目类型定义
#include "../../open-trade-common/types.h"
// 使用相同的Instrument结构
typedef boost::interprocess::allocator<
std::pair<const InsIdType, Instrument>,
boost::interprocess::managed_shared_memory::segment_manager> ShmemAllocator;
typedef boost::interprocess::map<
InsIdType, Instrument, CharArrayComparer, ShmemAllocator> InsMapType;- 独立性: 使用专用共享内存段
qamddata,避免与主项目冲突 - 兼容性: 数据结构与主项目保持一致,便于后续集成
- 扩展性: 预留接口,支持将来与主项目共享内存合并
# 1. 系统资源优化
# 增加文件描述符限制
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf
# 优化网络参数
echo 'net.core.rmem_max = 268435456' >> /etc/sysctl.conf
echo 'net.core.wmem_max = 268435456' >> /etc/sysctl.conf
echo 'net.core.netdev_max_backlog = 5000' >> /etc/sysctl.conf
sysctl -p
# 2. 目录权限和结构
mkdir -p /opt/quantaxis/qactpmdgateway/{config,logs,ctpflow,backup}
chown -R quantaxis:quantaxis /opt/quantaxis/
# 3. 生产配置文件
cp config/multi_ctp_config.json /opt/quantaxis/qactpmdgateway/config/production.json
# 根据实际环境调整配置参数# systemd服务配置文件 (/etc/systemd/system/qactpmd.service)
[Unit]
Description=QuantAxis Multi-CTP Market Data Gateway
After=network.target
[Service]
Type=simple
User=quantaxis
WorkingDirectory=/opt/quantaxis/qactpmdgateway
ExecStart=/opt/quantaxis/qactpmdgateway/bin/market_data_server --config /opt/quantaxis/qactpmdgateway/config/production.json
Restart=always
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=qactpmd
[Install]
WantedBy=multi-user.target
# 启用和管理服务
sudo systemctl enable qactpmd
sudo systemctl start qactpmd
sudo systemctl status qactpmd# 监控脚本 (monitor_multi_ctp.sh)
#!/bin/bash
ALERT_EMAIL="[email protected]"
LOG_FILE="/var/log/qactpmd_monitor.log"
# 检查服务状态
if ! systemctl is-active qactpmd >/dev/null; then
echo "$(date): Service down, restarting..." >> $LOG_FILE
systemctl restart qactpmd
echo "Multi-CTP service restarted" | mail -s "Alert: Service Restart" $ALERT_EMAIL
fi
# 检查连接数
active_conns=$(ls -d /opt/quantaxis/qactpmdgateway/ctpflow/*/ 2>/dev/null | wc -l)
if [ $active_conns -lt 50 ]; then
echo "$(date): Low connection count: $active_conns" >> $LOG_FILE
echo "CTP connections below threshold: $active_conns" | mail -s "Alert: Low Connections" $ALERT_EMAIL
fi
# 添加到crontab
# */5 * * * * /opt/quantaxis/scripts/monitor_multi_ctp.sh# 每日备份脚本
#!/bin/bash
BACKUP_DIR="/backup/qactpmd/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR
# 备份配置文件
cp -r /opt/quantaxis/qactpmdgateway/config $BACKUP_DIR/
# 备份CTP Flow文件
tar -czf $BACKUP_DIR/ctpflow_$(date +%H%M).tar.gz -C /opt/quantaxis/qactpmdgateway ctpflow/
# 清理30天前的备份
find /backup/qactpmd/ -type d -mtime +30 -exec rm -rf {} \;# 建议的启动顺序
# 1. 启动Redis服务
sudo systemctl start redis-server
# 2. 启动多CTP网关服务
sudo systemctl start qactpmd
# 3. 验证服务状态
systemctl status qactpmd
./bin/market_data_server --config config/production.json --status
# 4. 验证WebSocket连接
curl --include --no-buffer --header "Connection: Upgrade" \
--header "Upgrade: websocket" \
--header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
--header "Sec-WebSocket-Version: 13" \
http://localhost:7799/- 支持更多交易所(中金所、大商所、郑商所)
- 增加行情数据持久化功能
- 实现WebSocket客户端认证机制
- 添加行情数据压缩传输
- 支持Level-2深度行情
- 集成技术指标计算
- 实现行情数据回放功能
- 添加监控面板和管理界面
- 与主项目完全集成
- 支持分布式部署
- 实现行情数据分析引擎
- 提供REST API接口
- 完成基础架构设计和实现
- CTP API集成完成
- WebSocket服务器实现
- 智能订阅管理机制
- 独立共享内存管理
- 完整的错误处理机制
- 多CTP连接架构 - 支持90+期货公司同时连接
- 智能负载均衡 - 4种分发策略,25000+合约并发
- 故障自动转移 - 连接断开自动迁移订阅
- Redis数据缓存 - 完整实现,支持String最新行情 + ZSet历史tick数据
- 实时数据存储(String)
- 历史时间序列(ZSet,10万条自动清理2天前数据)
- 线程安全连接池
- 双协议支持 - 同时支持aid协议(QIFI体系)和action协议(兼容性)
- aid协议:长轮询 + 增量diff推送机制
- action协议:传统主动推送模式
- 配置化管理 - JSON配置文件,灵活部署
- 压力测试工具 - 多CTP系统专用测试客户端
- 工业级稳定性 - 生产环境高可用架构
- Level-2深度行情支持
- 更多交易所接入 (中金所、大商所、郑商所)
- WebSocket客户端认证
- 监控面板和管理界面
Copyright (c) QuantAxis. All rights reserved.
本项目是QuantAxis交易网关系统的一部分,遵循项目整体许可证条款。
如遇到技术问题,请通过以下方式获取支持:
- GitHub Issues: 提交问题报告和功能请求
- 技术文档: 查阅项目Wiki和API文档
- 社区交流: 加入QuantAxis技术交流群
- 商业支持: 联系QuantAxis技术支持团队
联系方式: [email protected]
项目提供了多个Python测试客户端,用于验证WebSocket服务器功能和演示API使用方法。
# 安装Python依赖
./install_python_deps.sh
# 多CTP系统压力测试 (测试71个合约)
python3 test_multi_ctp.py
# 指定服务器地址
python3 test_multi_ctp.py ws://localhost:7799
# 测试功能包括:
# - 大量订阅分发测试 (71个合约)
# - 多连接负载均衡验证
# - 故障转移测试
# - 连接质量监控# 快速测试(自动订阅rb2601)
python3 quick_test.py
# 指定服务器地址
python3 quick_test.py ws://localhost:7799# 使用默认配置
python3 test_client.py
# 指定服务器和合约
python3 test_client.py ws://localhost:7799 rb2601,i2501
# 交互模式命令
sub rb2601 # 订阅合约
unsub rb2601 # 取消订阅
list # 查询所有合约
search rb # 搜索合约
status # 查看状态
help # 显示帮助
quit # 退出# 适合不支持asyncio的环境
python3 simple_test_client.py
# 指定服务器
python3 simple_test_client.py ws://localhost:7799| 功能 | quick_test.py | simple_test_client.py | test_client.py | test_multi_ctp.py |
|---|---|---|---|---|
| 快速测试 | ✅ | ❌ | ❌ | ❌ |
| 交互模式 | ❌ | ✅ | ✅ | ❌ |
| 异步处理 | ❌ | ❌ | ✅ | ✅ |
| 多CTP压力测试 | ❌ | ❌ | ❌ | ✅ |
| 负载均衡验证 | ❌ | ❌ | ❌ | ✅ |
| 故障转移测试 | ❌ | ❌ | ❌ | ✅ |
| 连接监控 | ❌ | ❌ | ❌ | ✅ |
| 格式化显示 | 简单 | 详细 | 详细 | 统计 |
| 依赖库 | websocket-client | websocket-client | websockets | websockets |
| 适用场景 | 快速验证 | 一般测试 | 生产使用 | 多CTP系统测试 |
🚀 【rb2601】实时行情 14:30:15
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💰 最新价: 4180.00 📈 (+10.00, +0.24%)
📊 昨结算: 4170.00
📈 开盘价: 4175.00 | 最高: 4185.00 | 最低: 4172.00
📋 成交量: 12,580 | 成交额: 525,490,000
🏦 持仓量: 156,890
📏 涨停: 4587.00 | 跌停: 3753.00
📋 买一: 4179.00(10) | 卖一: 4180.00(8)
⏰ 更新时间: 20231201 09:30:15.500
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━