数据接口与获取:主流Level2数据源对比

做短线交易,数据就是你的眼睛。Level2行情比普通行情快那么零点几秒,但就是这零点几秒,决定了你是吃肉还是喝汤。今天咱们聊聊数据源怎么选、怎么接、怎么存。

主流Level2数据源:券商 vs 第三方

我个人习惯把数据源分成两类:券商自带的,和第三方提供的。各有各的脾气。

对比维度 券商Level2 第三方Level2
数据延迟 约200-500ms 约50-200ms
接口稳定性 高(券商自营) 中(依赖第三方服务器)
接入成本 低(开户即可) 高(年费几千到几万)
数据完整性 完整(交易所直连) 可能缺字段
编程友好度 差(多为C++/C#) 好(Python SDK)

嗯,这里要注意。券商的数据虽然稳定,但接口通常很「原始」。我最早用某券商的C++接口,光编译环境就折腾了两天。第三方数据源虽然贵,但人家把脏活累活都干了,你直接pip install就能用。

我的建议:实盘用券商数据源,回测和开发用第三方。为什么?实盘时你不想因为第三方服务器宕机而错过行情。回测时你更看重数据质量和历史长度。

Python接口对接实战

咱们直接上代码。以某第三方数据源为例,它的Python接口大概是这样的:

import level2_api as l2

# 初始化连接
client = l2.Client(
    api_key="your_api_key",
    secret_key="your_secret",
    data_type="level2"
)

# 订阅个股行情
client.subscribe(["600519.SH", "000001.SZ"])

# 回调函数处理实时数据
def on_tick(data):
    print(f"时间: {data['time']}")
    print(f"最新价: {data['last_price']}")
    print(f"买一档: {data['bid_prices'][0]} x {data['bid_volumes'][0]}")
    print(f"卖一档: {data['ask_prices'][0]} x {data['ask_volumes'][0]}")
    # 这里可以加你的策略逻辑

client.on_tick = on_tick
client.start()

这段代码看着简单,但坑不少。我曾经在项目中遇到过一个问题:回调函数里处理数据太慢,导致数据堆积,内存爆了。后来加了队列缓冲才解决。

避坑指南:回调函数里千万别做耗时操作。把数据扔进队列,另起线程处理。我曾经因为直接在回调里写数据库,导致行情延迟从50ms飙升到2秒。

再说说券商接口。券商的Level2接口通常走TCP长连接,数据是二进制流。你需要自己解析协议。举个例子:

import socket
import struct

# 连接券商行情服务器
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("192.168.1.100", 8888))

# 发送订阅请求
subscribe_packet = build_subscribe_packet(["600519"])
sock.send(subscribe_packet)

# 循环接收数据
while True:
    header = sock.recv(16)  # 固定头部16字节
    body_len = struct.unpack("I", header[12:16])[0]
    body = sock.recv(body_len)
    tick = parse_level2_tick(body)
    print(tick)

你看,券商接口就是这种风格。你得自己拼包、拆包、处理粘包。说白了,就是回到了C语言时代。但好处是延迟确实低,毕竟少了一层转发。

数据缓存与存储

数据拿到手了,怎么存?这是个大学问。短线交易的数据量有多大?你想想看,一只股票一天产生约4000笔Tick数据,全市场4000多只股票,一天就是1600万条记录。这还不算盘口快照。

我建议分三层存储:

  1. 内存缓存:存最近1分钟的Tick数据,用于实时计算
  2. 本地文件:存当天的完整数据,用于盘后复盘
  3. 数据库:存历史数据,用于回测和策略优化

内存缓存我用Redis,速度快,还支持过期自动清理。代码大概这样:

import redis
import json

r = redis.Redis(host='localhost', port=6379, db=0)

def cache_tick(stock_code, tick_data):
    key = f"tick:{stock_code}:{tick_data['time']}"
    r.setex(key, 60, json.dumps(tick_data))  # 60秒过期

def get_latest_tick(stock_code):
    keys = r.keys(f"tick:{stock_code}:*")
    if not keys:
        return None
    latest_key = max(keys)
    return json.loads(r.get(latest_key))

本地文件存储,我推荐用Parquet格式。比CSV快10倍,压缩率也高。每天收盘后,把当天的Tick数据转成Parquet存起来。

import pandas as pd

# 假设tick_list是当天的所有Tick数据
df = pd.DataFrame(tick_list)
df.to_parquet(f"data/2024-01-15_tick.parquet", compression="snappy")

数据库层面,我踩过不少坑。最开始用MySQL,写入速度跟不上。后来换了ClickHouse,专门为时序数据优化的,写入速度能达到每秒百万行。

注意:千万别把Tick数据直接往关系型数据库里怼。我见过有人用MySQL存Tick,一天下来表就几千万行,查询一条数据要几十秒。时序数据库才是正解。

最后,画一张图总结一下整个数据流转:

Level2行情数据流转架构 交易所 券商Level2接口 / 第三方Level2接口 Python对接层 内存缓存 (Redis) 最近1分钟Tick数据 本地文件 (Parquet) 当天完整数据 时序数据库 (ClickHouse) 历史数据 实时策略计算 / 盘后复盘 / 历史回测

这张图把整个数据流转讲清楚了。从交易所出来,经过券商或第三方接口,到Python对接层,然后分三路存储。最后,策略程序从不同存储层取数据做计算。

我个人习惯是:实时策略直接从Redis取数据,延迟最低。盘后复盘用Parquet文件,查询灵活。历史回测用ClickHouse,数据量大也不怕。

核心要点:数据接口选型要看你的交易频率。高频交易(毫秒级)必须用券商直连。中低频(秒级)用第三方完全够用。存储方案要分层,别把所有鸡蛋放一个篮子里。

蓝海数据掘金营,专注资料整理