简介:面向高频交易与量化投资者的同花顺Level-2全推行情Python API操作手册,聚焦沪深市场深度行情获取与涨停板策略等实战场景,适合具备Python基础、希望打通行情接入到策略落地环节的中高级用户。压缩包共1个PDF文件,约850KB,以版本修订说明与接口文档为主体,便于随查随用。已有2833人学习下载。手册系统梳理了从V2.0.0多连接模式改造、上证逐笔委托接入,到级联支持、Level-1数据兼容、Linux适配及异步订阅接口等历次迭代,并详解TDS的C/S架构、运行环境要求与初始化流程;对五档与十档快照、逐笔成交与委托、委托队列、极速盘口等数据类型及回调注册方式均有说明,可帮助读者快速理解字段变更、搭建低延迟行情链路并规避常见接入问题。
1. 拆开这份 Level-2 全推行情 Python SDK:量化人拿到手第一件事做什么
做涨停板策略的朋友大概都有过这种体验:盘口五档看着挺热闹,真到封板那一瞬间,你根本不知道前面排了多少单、撤了多少单、逐笔委托里大资金是挂进来还是抽走了。五档快照给不了这个信息,逐笔成交和逐笔委托才给得了。这份《同花顺 level-2 全推行情 Python API 操作手册》对应的就是解决这个问题的东西——一套 C/S 结构的行情 SDK,Python 侧通过datafeed包接入,能拿沪深两市的 Level-1、Level-2 快照、逐笔成交、逐笔委托、委托队列,还有需要额外权限的极速盘口。它适合谁?适合已经能写 Python、想自己搭行情落库或实时策略链路、又不满足于免费五档延迟数据的从业者。不适合纯小白拿来做第一份数据源,因为账号、权限、网络环境这几关就得先过。下面按我实际拆包和跑通的顺序讲,重点放在参数怎么设、回调怎么写、坑在哪。
2. 环境与初始化:从 pip 装包到 set_env 的六个关键参数
2.1 装包与导入验证
拿到的是L2-tdsapi-python-X.X.X-XXXXXXXX.tar.gz这种命名格式的包,Linux 下要 root 权限装。我一般先建独立虚拟环境,避免和系统 Python 的依赖打架。
# 建虚拟环境,Python 3.5(64位)及以上,实际建议 3.8+ python3 -m venv l2env source l2env/bin/activate # 方式一:直接 pip 装 tar.gz pip install ./L2-tdsapi-python-X.X.X-XXXXXXXX.tar.gz # 方式二:解压后 setup.py 安装(老包常见) tar -xzf L2-tdsapi-python-X.X.X-XXXXXXXX.tar.gz cd L2-tdsapi-python-X.X.X-XXXXXXXX python setup.py install装完别急着写策略,先验证导入。这一步看着废话,但我见过太多人在import datafeed报ModuleNotFoundError之后去怀疑账号,其实是装到了另一个 Python 解释器里。
# verify_install.py import datafeed from datafeed.tds_api import TDS print("datafeed 导入成功") tds = TDS() print("TDS 实例创建成功:", tds)import datafeed不抛异常就算装好了。TDS()创建实例本身不连网,所以这一步失败基本是包或解释器问题,不是网络问题。硬件上官方要求 64 位双核、8G 内存、下行 100Mb 以上,实测跑全市场逐笔落库,内存和磁盘 IO 才是真瓶颈,8G 是底线不是舒适线。
2.2 set_env:回调注册和连接参数一次说清
set_env必须在创建实例之后、登录之前调用,而且必须先调它才能调后续的注册和登录。它的参数分两类:一类是连接行为参数,一类是回调函数注册。连接参数里最值得关注的是这几个:
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
m_heartbeat_interval | int | 系统默认 | 心跳间隔秒数,最小 1,小于 1 用默认 |
m_reconn_count | int | 系统默认 | 断线重试次数,小于 1 不重连 |
m_reconn_interval | int | 系统默认 | 重连间隔秒数,小于 1 立即重连 |
m_timeout | int | 5000 | 同步请求超时,单位毫秒 |
m_connectmarket | str | "USH;USZ" | 连接市场,可只填USH或USZ |
m_connectsubscribe | int | 0 | 1 表示重登录成功后自动重发订阅 |
m_connectmarket是 V2.1.0 之后才有的,只做单市场策略时填单市场能少一半连接开销。m_connectsubscribe建议设 1,否则断线重连后你得自己把订阅全重发一遍,漏一个代码就静默少数据,这种问题排查起来非常费劲。
回调注册这块,set_env接受fnctickindex(指数快照)、fnctickstock(证券快照)、fncorderqueue(委托队列)、fncorderdata(逐笔委托)、fnctransaction(逐笔成交)、fncsuperstock(极速盘口)六个数据回调,外加fncnotify(断线通知)和fucsubscribe(异步订阅回调)。每个数据回调签名固定是(data_list, count, stats)三个参数,少一个都注册不上。
from datafeed.tds_api import TDS tds = TDS() def on_stock(data_list: list, count: int, stats: dict): # data_list 是本次推送的行情列表,可能多条 # stats 是链路时间戳字典,用于算延迟 for row in data_list: print(row["code"], row["new_price"], row["bidorder_price"][:3]) def on_notify(msg_notify: dict): # code: 1 网络断开 2 网卡异常 3 重复登录被踢 print("断线通知:", msg_notify["code"], msg_notify["msg"]) tds.set_env( fnctickstock=on_stock, fncnotify=on_notify, m_connectmarket="USH;USZ", m_connectsubscribe=1, m_reconn_count=5, m_reconn_interval=3, m_timeout=5000, )stats里三个时间戳produce_time、send_time、recv_time都是微秒级 unix 时间,recv_time - produce_time就是端到端延迟,做高频的应该把它落库,别只看行情不看链路。回调里绝对不要做耗时操作,官方明确说了会导致堵包,我一般只做一件事:把数据塞进queue.Queue或直接批量写内存缓冲,真正的处理放到另一个线程。
3. 登录、查询与订阅:把回调数据真正接进策略
3.1 登录返回值:错误码就是排错地图
login(username, password)成功返回 0,失败返回负数。这份手册把错误码列得很全,实际排错时它就是地图,我挑几个高频的:
| 返回码 | 含义 | 我一般怎么处理 |
|---|---|---|
| -1004 | 用户名密码错误 | 先查账号,别急着怀疑网络 |
| -1005 | 无法连接至服务器 | 查域名端口、防火墙、m_connectmarket |
| -1013 | 登录验证失败(服务端) | 多为权限或账号状态问题 |
| -1016 | 重复登录 | 同一账号别开两个实例 |
| -1018 | 版本太低 | 升级 SDK,V2.1.3 起专门加了这个码 |
ret = tds.login("your_account", "your_password") if ret != 0: raise RuntimeError(f"登录失败,返回码 {ret}") print("登录成功")-1018是 V2.1.3 才加的,意思是服务端认为你 SDK 版本过低。遇到它别去改代码,直接换新包。-1016重复登录在调试时特别容易撞上——上一个进程没退干净,新进程登录就被踢,表现是"登录成功但收不到数据",其实是旧连接还占着。
3.2 同步查询:先拿代码表再取快照
查询接口是同步的,适合做初始化、补数据、对账。get_code_list(market)拿代码表,get_tick_stock(market, code_list)拿证券快照,get_tick_index拿指数快照,get_super_stock拿极速盘口。code_list传[]表示全市场。
# 拿上交所指数代码表 index_codes = tds.get_code_list("USHI") print("指数代码数:", len(index_codes)) # 拿 USHA 全市场证券快照([] 表示全市场) snapshots = tds.get_tick_stock("USHA", []) print("快照条数:", len(snapshots)) # 拿单只股票的极速盘口(需产品权限) super_stock = tds.get_super_stock("USHA", ["600000"])市场号这块必须记牢,填错市场返回空列表,不报错,非常隐蔽。上交所:USHI指数、USHAA 股、USHBB 股、USHD债券、USHJ基金、USHP退市整理、USHTST 风险警示板;深交所:USZI指数、USZAA 股、USZBB 股、USZD债券、USZJ基金、USZP退市整理。上证 A 股还包括USHT,做涨停板策略如果只订USHA,ST 板的票就漏了,这是血泪经验。
3.3 订阅:同步与异步两条路
订阅接口有四个同步的:sub(取消该市场该类型全部订阅并重新订阅)、unsub(取消全部)、add_sub(增加指定代码)、del_sub(取消指定代码)。V2.1.4 之后多了async_sub,通过fucsubscribe回调通知结果,适合订阅量大、不想阻塞主线程的场景。
# 同步订阅:USHA 全市场证券快照 ret = tds.sub("stock", "USHA", []) print("订阅返回:", ret) # 0 成功 # 增加 USHA 全市场逐笔成交 tds.add_sub("trans", "USHA", []) # 异步订阅:全新订阅 USHA 全市场逐笔委托 tds.async_sub("order", 1, "USHA", [])async_sub的op_type:1 全新订阅、2 取消订阅、3 增加订阅代码、4 取消订阅代码。结果走fucsubscribe(data_type, op_type, market, code_list, statuscode, callback_type),statuscode为 0 才算成功。callback_type为 1 表示异步订阅触发,为 2 表示断线重连或手动登录后自动重发订阅触发——这个区分很有用,重连后如果某个订阅statuscode非 0,说明那条订阅没恢复,得手动补。
数据类型字符串就六个:index、stock、queue、order、trans、superstock。注意trans是逐笔成交不是逐笔交易,order是逐笔委托,别写混。
3.4 快照字段:涨停板策略真正要盯的几个
证券快照字段很多,做涨停板我重点看这几组:bidorder_price/bidorder_volume买一到买十、askorder_price/askorder_volume卖一到卖十、bid_numorders/ask_numorders各档委托笔数、totalbid_volume/totalask_volume总委买委卖量、avgbid_price/avgask_price加权平均委买委卖价。沪市还额外给withdraw_buynum、withdraw_buyvolume、withdraw_buyturnover撤单三件套,以及totalbid_num、totalask_num总笔数、bidorder_num、askorder_num委托价位数。
封板判断上,totalbid_volume和买一量对比能看出封单厚度,withdraw_buyvolume突然放大说明封单在撤,bid_numorders买一委托笔数暴增往往是散户排队。深市 2022.5.16 之后快照字段是所有交易方式汇总,额外给matching_volume、matching_turnover、matching_price、deal_mode、mode_state五个字段,deal_mode为 1 才是匹配成交(原竞价交易),做涨停板要按deal_mode过滤,否则会把协商成交、盘后交易混进来。
status字段是状态机,上证四位、深证两位,格式不统一。上证第 1 位T是连续交易、U是收盘集合竞价、P是停牌;深证第 1 位T连续竞价、C收盘集合竞价、H临时停牌。解析时按市场分支写,别用一套逻辑套两个市场。
4. 级联与离线调试:多实例分发和本地回放的落地细节
4.1 级联模式:主从 SDK 的配置与注册顺序
级联解决的是"一个账号、多个策略进程"的问题。主 SDK 连行情服务器、收数据、往局域网分发,从 SDK 连主 SDK 拿数据。开启分两步:配置文件切模式,从 SDK 调register_addr。
配置文件叫tool.ini,Python 版放在程序文件同级目录。两个字段:master(true/false,默认 true)和socket_port(监听端口)。端口号后面必须接换行符,否则参数读取失败——这个坑很隐蔽,配置文件看着没问题就是不生效。
# 主 SDK tool.ini master=true socket_port=9999# 从 SDK tool.ini master=false socket_port=9998主从同机时两个socket_port必须不同。从 SDK 在登录前调register_addr("master", "主SDK的IP", 9999),然后login,账号密码要和主 SDK 一致。
from datafeed.tds_api import TDS tds = TDS() tds.set_env(fnctickstock=on_stock) # 从 SDK:先注册主 SDK 地址,再登录 ret = tds.register_addr("master", "192.168.1.10", 9999) if ret != 0: raise RuntimeError(f"注册主 SDK 失败: {ret}") tds.login("your_account", "your_password")顺序不能反:必须主 SDK 登录成功后,从 SDK 才能连;register_addr在login之前调。级联模式下主 IP 连接异常返回-1014,主从不在一局域网返回-1015。V2.0.8 做过级联性能优化,V2.1.6 又优化了一次,多实例场景尽量用新版本。
4.2 离线调试:没有行情时也能把链路跑通
离线调试是 V2.1.0 加的,价值在于:策略逻辑、回调处理、落库链路可以在无实时行情时用本地 CSV 回放验证。调用时机是实例创建之后、登录之前,调了之后login不连线上网关直接返回成功,完全离线。
from datafeed.tds_api import TDS tds = TDS() tds.set_env(fnctickstock=on_stock) # 进入离线调试:路径、文件类型、推送间隔ms、单次条数、循环次数 ret = tds.start_offline_debug( path="D:\\datafeed", subtype=["stock", "index", "order", "queue", "trans", "superstock"], push_space=3000, push_count=5000, playback_times=1, ) print("离线调试返回:", ret) tds.login("test1", "123456") # 不连 server数据文件命名是硬约束,必须是stock.csv、index.csv、order.csv、queue.csv、trans.csv、superstock.csv、codelist.ini之一,SDK 靠文件名判断数据类型,命名不规范直接不读。CSV 表头字段名参照模板,顺序不强制。回放规则:按subtype顺序读文件,每次读push_count条,间隔push_space毫秒,所有文件遍历playback_times次后结束;当次数据不含订阅代码就不触发回调,和实时行为一致。查询接口在离线模式下读codelist.ini和对应 CSV 前 3000 条(指数前 300 条)去重后供查询。级联模式下父节点可开离线调试,子节点能拿到数据,但子节点自己开离线调试无效。
5. 避坑清单:五个让我返工过的具体问题
5.1 回调里写数据库,跑几分钟就堵包
现象:实时行情跑几分钟后数据延迟越来越大,最后干脆不推了,日志里没有报错。原因:在fnctickstock回调里直接INSERT数据库或做复杂计算,回调线程被阻塞,SDK 内部缓冲堆满就堵包。解决:回调只做入队,queue.Queue(maxsize=...)或内存 list 加锁,另起消费线程批量落库。回调里连print都尽量少,高频下print本身就是瓶颈。
5.2 只订 USHA,ST 板涨停数据全漏
现象:涨停板策略回测发现某些 ST 票完全没有数据,实盘也收不到。原因:上证 A 股拆成USHA和USHT(风险警示板)两个市场号,只订USHA自然漏USHT。解决:sub("stock", "USHA", [])之后再补一条sub("stock", "USHT", []),逐笔同理。深市没有这个拆分,但USZA之外还有USZI指数,别混。
5.3 断线重连后订阅静默丢失
现象:网络抖动一次后,部分代码再也没有行情,但登录状态显示正常。原因:m_connectsubscribe没设 1,重登录后 SDK 不自动重发订阅;或者设了 1 但某条订阅在服务端失败,fucsubscribe的statuscode非 0 没人看。解决:set_env里m_connectsubscribe=1,同时注册fucsubscribe,在callback_type=2的回调里检查statuscode,非 0 的订阅手动add_sub补发。
5.4 tool.ini 端口后少个换行,级联死活不生效
现象:主从配置都写了,从 SDK 就是连不上主 SDK,返回-1014或直接失败。原因:socket_port=9999后面没接换行符,配置解析失败,SDK 退回默认非级联模式。解决:用编辑器确认文件末尾有换行,master和socket_port各占一行,别写成一行。改完重启进程,配置只在启动时读一次。
5.5 深市快照不按 deal_mode 过滤,成交量对不上
现象:自己统计的成交量和行情软件对不上,偏差还不小。原因:2022.5.16 后深市快照字段是所有交易方式汇总,volume含协商成交、盘后交易等,直接拿来算竞价成交量就偏大。解决:用matching_volume、matching_turnover算匹配成交(原竞价),用deal_mode判断最近一笔成交方式,mode_state解析各交易方式状态。deal_mode为 1 才是匹配成交,2 协商、3 点击、4 询价、5 竞买。
6. 进阶:用 stats 时间戳和离线回放把策略验证闭环
真正把这份 SDK 用出价值,不是收到数据就完事,而是能证明"我的策略在真实链路延迟下依然成立"。我一般做两件事:一是把stats里的produce_time、send_time、recv_time全部落库,按recv_time - produce_time算端到端延迟分布,看 P99 是多少;二是用离线调试把同一份 CSV 回放多遍,对比策略信号在"零延迟"和"真实延迟"下的差异。
import time from collections import deque latency_window = deque(maxlen=10000) def on_stock(data_list, count, stats): now = stats["recv_time"] latency_window.append(now - stats["produce_time"]) # 入队,交给消费线程 for row in data_list: raw_queue.put(row) def latency_p99(): if not latency_window: return None arr = sorted(latency_window) idx = int(len(arr) * 0.99) return arr[min(idx, len(arr) - 1)] / 1000.0 # 转毫秒produce_time是服务接收到行情的时间,send_time是服务发送时间,recv_time是 SDK 接收时间,三者都是微秒。send_time - produce_time是服务端处理耗时,recv_time - send_time是网络传输耗时,分开看才知道延迟出在哪一段。如果网络段 P99 突然抬高,先查本地带宽和网卡,别急着改策略参数。
离线回放验证时,push_space设成你实测的 P99 延迟,push_count设成单次回调的典型条数,这样回放出来的节奏最接近实盘。我习惯先用push_space=0跑一遍拿"理想信号",再用实测延迟跑一遍,两个信号序列的差异就是延迟对策略的真实影响。差异大的策略,实盘大概率翻车。
还有一个容易忽略的点:m_timeout默认 5000 毫秒,同步查询在行情高峰偶尔会超时返回负值,get_tick_stock返回空列表时先看是不是超时,别直接当成"没数据"。我一般把同步查询放在初始化阶段,实时链路全走订阅回调,减少同步接口在高峰期的暴露。
从那以后我每次接新行情源,都强制先跑一遍离线回放加延迟统计,确认回调不堵、订阅不漏、字段口径对得上,再上实盘。这套流程帮我挡掉过好几次"看着能跑、一上量就崩"的问题。希望帮到你。
本文还有配套的精品资源,点击获取