2. Level2 行情数据获取:主流数据源对比与接入实践
Level2 行情数据的获取是整个策略评估与推演体系的基石。数据源的稳定性、实时性、字段完整度以及接入成本,直接决定了后续策略回测与沙盘推演的可靠性。本章将从数据源对比、API 认证鉴权、实时流与历史数据拉取三个维度,系统梳理主流接入方案。
2.1 主流数据源对比:券商 vs 第三方数据商
目前国内 Level2 行情数据的主要提供方分为两类:券商自研数据服务(如华泰、中信、国泰君安)与第三方数据商(如万得 Wind、聚宽、Tushare、掘金量化)。两者在数据质量、延迟、费用与开发友好度上存在显著差异。
| 对比维度 | 券商自研数据服务 | 第三方数据商 |
|---|---|---|
| 数据源权威性 | 直接来自交易所授权,原始报文级 | 多数为二次加工或转售,存在字段裁剪 |
| 实时性(延迟) | 通常 < 50ms(极速交易通道可达 10ms) | 100ms ~ 500ms,受中间链路影响 |
| 字段完整度 | 全量逐笔委托、逐笔成交、十档行情 | 部分仅提供快照级数据,缺少逐笔还原 |
| 历史数据深度 | 通常仅保留近 1~3 个月 | 可提供 3~5 年甚至全量历史 |
| 接入门槛 | 需开通券商交易账户,部分要求资产门槛 | 注册即用,部分提供免费额度 |
| 费用模式 | 按年收取行情费(约 3000~8000 元/年) | 按调用量或订阅套餐(免费~数万元/年) |
| 开发文档质量 | 偏内部接口,文档晦涩,示例少 | 文档完善,SDK 丰富,社区活跃 |
选型建议: 高频策略(Tick 级、逐笔还原)优先选择券商直连;中低频策略或研究型场景,第三方数据商在历史数据深度与开发效率上更具优势。
2.2 API 接口认证与鉴权机制
无论选择哪种数据源,接入 Level2 行情 API 都必须通过认证与鉴权。主流方案分为 Token 令牌 与 双向 SSL 证书 两类。
2.2.1 Token 令牌模式(第三方数据商为主)
- 获取方式: 注册后从控制台生成固定 Token,或通过 OAuth2.0 动态获取。
- 传递方式: 通常放在 HTTP Header 的
Authorization: Bearer <token>中。 - 有效期: 固定 Token 长期有效;动态 Token 通常 24h 过期,需定时刷新。
- 安全建议: Token 不要硬编码在代码中,建议通过环境变量或密钥管理服务注入。
# Python 示例:使用 Token 请求 Level2 快照数据
import requests
TOKEN = "your_level2_token_here"
headers = {"Authorization": f"Bearer {TOKEN}"}
url = "https://api.datasource.com/v2/level2/snapshot?symbol=000001.SZ"
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
print("最新卖一价:", data["ask_price"][0])
else:
print("鉴权失败:", response.status_code, response.text)
2.2.2 双向 SSL 证书模式(券商直连为主)
- 原理: 客户端与服务端各持一份数字证书,建立 TLS 双向认证通道。
- 文件组成: 客户端证书(
.p12或.pem)、私钥密码、CA 根证书。 - 适用场景: 极速交易柜台、券商内部量化平台,安全性最高。
- 注意事项: 证书到期前需手动续期,否则行情流会中断。
# Python 示例:使用双向 SSL 连接券商 WebSocket 行情
import ssl
import websocket
ssl_context = ssl.create_default_context()
ssl_context.load_cert_chain(
certfile="client_cert.pem",
keyfile="client_key.pem",
password="your_key_password"
)
ssl_context.load_verify_locations("ca_cert.pem")
ws = websocket.WebSocket(sslopt={"context": ssl_context})
ws.connect("wss://quote.broker.com/level2")
ws.send('{"action":"subscribe","symbols":["000001.SZ"]}')
print("WebSocket 双向认证连接成功")
避坑提示: 部分券商要求同时提供交易密码或动态口令(OTP)作为二次鉴权。务必在代码中处理鉴权失败的重试与告警逻辑,避免因 Token 过期导致策略空跑。
2.3 WebSocket 实时流与 RESTful 历史数据拉取
Level2 行情数据接入通常采用 双通道架构:WebSocket 负责实时流推送,RESTful API 负责历史数据补全与快照查询。两者在延迟、数据量与使用场景上互补。
2.3.1 WebSocket 实时流:订阅与推送
- 协议: 基于 WebSocket 全双工通信,支持二进制(Protocol Buffers)或 JSON 文本帧。
- 订阅模式: 客户端发送订阅请求(如
{"op":"subscribe","args":["tick.000001.SZ"]}),服务端持续推送增量数据。 - 心跳维持: 每 15~30 秒发送 Ping 帧,服务端回复 Pong,防止连接断开。
- 重连机制: 必须实现指数退避重连,避免断线后数据丢失。
# Python 示例:WebSocket 实时订阅 Level2 逐笔成交
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
if data["type"] == "tick":
print(f"时间:{data['time']} 价格:{data['price']} 量:{data['volume']}")
def on_error(ws, error):
print("WebSocket 错误:", error)
def on_close(ws, close_status_code, close_msg):
print("连接关闭,准备重连...")
def on_open(ws):
subscribe_msg = {
"op": "subscribe",
"args": ["tick.000001.SZ", "tick.600519.SH"]
}
ws.send(json.dumps(subscribe_msg))
print("订阅请求已发送")
ws = websocket.WebSocketApp(
"wss://quote.datasource.com/level2/ws",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever(ping_interval=20, ping_timeout=10)
2.3.2 RESTful 历史数据拉取:补全与回测
- 接口类型: 历史 Tick 数据、历史快照(1 分钟/5 分钟)、逐笔还原数据。
- 请求参数: 股票代码、起始时间、结束时间、数据粒度(tick / 1min / 5min)。
- 限流策略: 多数数据商限制每秒请求次数(如 5 次/秒),超出返回 429 状态码。
- 分页拉取: 单次返回数据量有限(如 1000 条),需通过
page_token或offset循环拉取。
# Python 示例:RESTful 拉取历史 Tick 数据(带分页)
import requests
import time
TOKEN = "your_token"
BASE_URL = "https://api.datasource.com/v2/level2/history"
headers = {"Authorization": f"Bearer {TOKEN}"}
def fetch_ticks(symbol, start, end, page_size=1000):
all_ticks = []
page_token = None
while True:
params = {
"symbol": symbol,
"start": start,
"end": end,
"page_size": page_size,
"page_token": page_token
}
resp = requests.get(BASE_URL, headers=headers, params=params)
if resp.status_code == 429:
time.sleep(1) # 限流等待
continue
data = resp.json()
all_ticks.extend(data["ticks"])
if not data.get("next_page_token"):
break
page_token = data["next_page_token"]
time.sleep(0.2) # 控制请求频率
return all_ticks
# 拉取 2025-01-10 当天全部 Tick
ticks = fetch_ticks("000001.SZ", "2025-01-10 09:30:00", "2025-01-10 15:00:00")
print(f"共获取 {len(ticks)} 条 Tick 数据")
2.3.3 双通道架构对比
| 特性 | WebSocket 实时流 | RESTful 历史数据 |
|---|---|---|
| 数据延迟 | 毫秒级推送 | 秒级~分钟级(取决于查询时间范围) |
| 数据量 | 仅推送增量,带宽占用低 | 单次可拉取数千条,适合批量处理 |
| 使用场景 | 实时监控、策略信号触发、沙盘推演 | 回测初始化、数据补全、盘后分析 |
| 连接状态 | 长连接,需心跳与重连 | 短连接,无状态 |
| 错误处理 | 断线重连 + 数据序列号校验 | HTTP 状态码 + 重试机制 |
最佳实践: 策略运行期间,WebSocket 负责接收实时行情驱动决策引擎;每日收盘后,通过 RESTful 接口拉取当日完整 Tick 数据,用于沙盘推演复盘与模型校准。两者结合可构建完整的数据闭环。
2.4 数据质量校验与异常处理
无论数据源多么可靠,网络抖动、交易所异常或数据商处理故障都可能导致数据质量问题。建议在接入层内置以下校验逻辑:
- 序列号连续性: 逐笔成交数据通常携带递增序列号,检测跳号即表示丢包。
- 时间戳单调性: 后一条数据的时间戳必须 ≥ 前一条,否则视为乱序。
- 价格/量合理性: 价格在 [0.01, 10000] 区间,单笔成交量不超过流通盘的 1%。
- 断线重连补偿: 重连后通过 RESTful 接口拉取缺失时间窗口的数据进行回补。
工程要点: 建议将数据校验结果写入独立日志文件,并设置告警阈值(如连续 10 次序列号跳号触发邮件/钉钉通知)。在沙盘推演中,数据质量日志也是复盘的重要依据。