简介:面向高频交易与量化投资者的同花顺Level-2全推行情Python API操作手册,聚焦沪深市场深度行情获取与涨停板策略等实战场景,适合具备Python基础、希望接入低延迟行情的中高级开发者。压缩包共1个PDF文件,约850KB,以版本修订说明与接口文档为主体,便于按版本脉络查阅。已有2833人学习下载,具备一定参考热度。手册完整梳理V2.0.0至V2.1.9的迭代轨迹:从单连接转多连接、支持上证逐笔委托,到级联功能、Level-1数据、Linux兼容、离线调试与异步订阅接口,并逐项说明快照字段扩充、数据类型精度调整及回调参数变更。正文涵盖TDS的C/S架构与高可用设计、运行环境要求、安装验证步骤,以及初始化实例、注册行情与断线通知回调、设置连接市场与超时、订阅五档十档快照、逐笔成交委托与委托队列等接口用法,可帮助读者快速搭建行情接入环境并规避常见配置问题。
1. 一份能跑通全推行情的 Python SDK,到底解决了什么
做涨停板策略的人最怕什么?不是模型不收敛,而是行情数据慢半拍、盘口只有五档、逐笔委托拿不到。你盯着五档快照做封单强度判断,结果主力在十档之外挂撤单,你根本看不见;你想统计逐笔委托里的撤单节奏,接口却只给你成交回报。这份《Level-2 全推行情 Python API 操作手册》对应的 SDK,就是冲着这几个痛点来的:沪深 Level-2 十档快照、逐笔成交、逐笔委托、委托队列、极速盘口,全部通过回调推给你,而不是让你轮询。它适合谁?适合已经能写 Python、手里有行情账号、想把涨停板封单结构和撤单行为做成因子的量化从业者。不适合只想拿日线做回测的新手,因为这套东西的价值全在盘中毫秒级的推送细节里。
2. 环境与初始化:把 TDS 实例跑起来的第一公里
2.1 安装与运行环境核对
这份 SDK 是 C/S 结构,Python 侧只是一个薄封装,真正的解码和分发在服务端完成,所以本地环境不用堆很高。手册给的硬件底线是 64 位双核、8G 内存、下行 100Mbps,操作系统 Windows7 以上或 CentOS 7 这类常见 Linux 发行版,Python 3.5(64 位)及以上。我一般会先确认 Python 位数,因为 32 位解释器加载 64 位动态库会直接报错,这个坑比想象中常见。
安装走的是本地包,不是从公共源拉:
# Linux 下需要 root 权限安装,Windows 用管理员权限的终端 pip install ./L2-tdsapi-python-X.X.X-XXXXXXXX.tar.gz # 如果 pip 装完导入异常,退回源码方式 python setup.py install装完先做最小验证,别急着写业务逻辑:
# 验证安装是否成功,没有异常就说明动态库加载正常 import datafeed print("datafeed imported ok")这里要说明一点:pip install和setup.py install二选一即可,前者适合干净环境,后者适合你已经手动解压、想看清包内文件结构的情况。验证阶段只导入datafeed顶层包,不导入tds_api,是为了把「包没装上」和「动态库依赖缺失」两类错误分开——如果import datafeed就炸,基本是安装路径或权限问题;如果这步过了、导入tds_api才炸,多半是动态库依赖没满足。
2.2 创建实例、注册回调与 set_env 的参数取舍
整个 SDK 是回调驱动的,工作流程是:创建TDS实例 →set_env注册回调并设环境参数 →login登录 →sub订阅 → 回调里收数据。顺序不能乱,set_env必须在注册和登录之前调用,这是硬约束。
from datafeed.tds_api import TDS # 1. 创建实例 tds = TDS() # 2. 定义行情推送回调,签名固定为 (data_list, count, stats) def callback_stock(data_list, count, stats): # data_list 是本次推送的数据列表,可能多条 # count 是条数,stats 是链路时间戳字典 print("recv count:", count) # 生产环境这里不要做重活,见 2.3 的堵包说明 # 3. 设置环境参数并注册证券快照回调 tds.set_env( fnctickstock=callback_stock, # 证券快照回调 m_heartbeat_interval=10, # 心跳间隔,秒,最小 1 m_reconn_count=5, # 断线重试次数,<1 不重连 m_reconn_interval=3, # 重连间隔,秒 m_timeout=5000, # 同步请求超时,毫秒 m_connectmarket="USH;USZ", # 连接市场,可只填单市场 m_connectsubscribe=1, # 重登后自动重发订阅 m_logpath="./tds_log", # 日志路径 m_loglevel="warn" # 日志级别 )set_env的参数里,真正影响稳定性的就三个:m_reconn_count、m_reconn_interval、m_connectsubscribe。m_connectsubscribe=1是我强烈建议开的,它让 SDK 在断线重连成功后自动把之前的订阅请求重发一遍,否则你半夜网络抖一下,第二天开盘发现订阅全丢了,这种血泪经验不值得重复。m_connectmarket默认"USH;USZ"双市场,如果你只做沪市涨停板,填"USH"能少一条连接、少一份资源占用。m_timeout只影响get_code_list、get_tick_stock这类同步查询,不影响推送回调。
回调函数有六个数据入口,对应关系要记牢:fnctickindex指数快照、fnctickstock证券快照、fncorderqueue委托队列、fncorderdata逐笔委托、fnctransaction逐笔成交、fncsuperstock极速盘口。极速盘口需要额外产品权限,没权限的账号注册了也收不到数据,别以为是接口写错了。
2.3 回调里为什么不能做重活
手册里有一句容易被忽略的话:不要在回调函数中做耗时很长的操作,否则很可能导致接收实时行情堵包。这不是客套。回调是在 SDK 的接收线程里同步执行的,你在里面写数据库、算因子、发 HTTP,接收线程就被你占住了,后面的数据包在缓冲区里堆积,表现出来就是「行情延迟越来越大」甚至「回调突然不触发了」。
常见做法是回调里只做一件事:把data_list塞进queue.Queue,业务线程另起一个消费者慢慢处理。这样接收线程永远轻量,堵包风险降到最低。
import queue import threading raw_queue = queue.Queue(maxsize=200000) def callback_stock(data_list, count, stats): # 回调里只入队,不做任何计算和 IO try: raw_queue.put_nowait((data_list, stats)) except queue.Full: # 队列满说明消费端跟不上,这里要记数告警而不是阻塞 pass def consumer(): while True: data_list, stats = raw_queue.get() # 真正的落库、因子计算放这里 process(data_list, stats) threading.Thread(target=consumer, daemon=True).start()stats字典里带produce_time、send_time、recv_time三个微秒级时间戳,这是你排查延迟的黑匣子:recv_time - send_time是链路耗时,send_time - produce_time是服务端处理耗时。涨停板策略对延迟敏感,我一般会在消费端持续统计这两个差值的分位数,一旦 P99 明显抬升,先查本地消费是否堵了,再查网络。
3. 登录、查询与订阅:把数据流真正接上
3.1 登录返回值与错误码排查
登录是同步调用,返回 0 成功,负数失败。手册列了一串错误码,实际排障时高频的就那么几个:-1004用户名密码错误、-1005无法连接服务器、-1016重复登录、-1018版本太低。-1016特别值得说,同一个账号在多处登录会互踢,你本地调试时忘了关上一个进程,新进程就登不上,现象是「账号密码明明对的却返回 -1016」,第一次遇到很容易怀疑人生。
ret = tds.login("your_account", "your_password") if ret != 0: # 按错误码分流处理,不要笼统打印 if ret == -1016: print("重复登录,检查是否有残留进程占用账号") elif ret == -1018: print("SDK 版本过低,需要更新安装包") else: print("login failed, code:", ret)登录成功后,func_notify这个断线通知回调才会真正有意义。它的机制是:断线后多次重连都失败,SDK 已停止工作,此时回调收到{"code": int, "msg": str},code为 1 表示网络断开、2 表示网卡不正常、3 表示重复登录被踢下线。收到这个通知,正确响应是重新调用login和订阅,而不是干等。
3.2 代码表与快照查询:市场号别填错
查询类接口都是同步的,适合盘前拉代码表、盘中补数据。市场号是最容易翻车的地方:上交所 A 股是USHA,但上证风险警示板是USHT,两者是分开的市场号;深交所 A 股是USZA。你做涨停板策略,ST 股和普通股在不同市场号下,漏了USHT就会漏掉一批标的。
# 拉取上证指数全部代码 index_codes = tds.get_code_list("USHI") # 拉取 USHA 全市场证券快照,[] 表示全市场 snapshots = tds.get_tick_stock("USHA", []) # 只查单只股票的快照 one = tds.get_tick_stock("USHA", code_list=["600000"]) # 极速盘口查询,需要产品权限 super_snap = tds.get_super_stock("USHA", code_list=["600000"])get_code_list返回字符串列表,失败返回空列表[];get_tick_stock返回字典列表。注意get_code_info在手册里标注「暂未实现」,别在它上面浪费时间。查询接口返回的是「当前时刻的一份数据」,不是流,做实时策略还是得靠订阅推送。
3.3 订阅接口的四种操作与异步订阅
订阅分同步和异步两套。同步的sub是「取消该市场该类型的所有订阅并重新订阅」,语义要看清——它不是增量添加,是全量重置。增量用add_sub,取消单个代码用del_sub,整体取消用unsub。
# 全量订阅 USHA 证券快照(会先清掉该市场该类型的旧订阅) tds.sub("stock", "USHA", []) # 增量添加 USHA 逐笔成交订阅 tds.add_sub("trans", "USHA", []) # 取消 USHA 单只股票的逐笔委托订阅 tds.del_sub("order", "USHA", ["600000"]) # 取消 USHA 全部快照订阅 tds.unsub("stock", "USHA")异步订阅async_sub是 V2.1.4 之后加的,好处是不阻塞调用线程,结果通过fucsubscribe注册的回调通知。回调参数是(data_type, op_type, market, code_list, statuscode, callback_type),statuscode为 0 表示成功,callback_type为 1 表示异步订阅触发、2 表示断线重连后自动重发触发。做多市场批量订阅时,异步接口能明显减少启动阶段的等待。
def on_subscribe(data_type, op_type, market, code_list, statuscode, callback_type): if statuscode != 0: print("subscribe failed:", data_type, market, statuscode) tds.set_env(fucsubscribe=on_subscribe) # op_type: 1 全新订阅 2 取消订阅 3 增加订阅代码 4 取消订阅代码 tds.async_sub("stock", 1, "USHA", [])4. 数据结构与字段:涨停板策略真正用得上的那些列
4.1 快照字段的分层理解
证券快照字段很多,但按用途可以分三层。第一层是价格量能:pre_price、open_price、high_price、low_price、new_price、volume、turnover、trade_num。第二层是盘口:Level-2 给到买一到买十的bidorder_price、bidorder_volume、bid_numorders,卖侧对称。第三层是沪市特有的撤单与委托统计:withdraw_buynum、withdraw_buyvolume、withdraw_buyturnover、withdraw_sellnum、withdraw_sellvol、withdraw_sellturnover,以及totalbid_num、totalask_num、bidorder_num、askorder_num。
对涨停板封单强度判断,第三层才是金矿。封单量看买一量,但封单的「真实性」要看撤单:买一挂着巨量、同时withdraw_buynum持续攀升,说明有资金在反复挂撤制造虚假封单。这个组合信号,五档快照根本给不了。
4.2 status 与 mode_state 的状态机解读
status字段是字符串,沪深编码规则不同,这是最容易解析错的地方。上证第 1 位:S 启动、C 开盘集合竞价、T 连续交易、E 闭市、P 停牌、M 可恢复熔断、N 不可恢复熔断、U 收盘集合竞价;第 2 位 0/1 表示能否正常交易;第 3 位 0/1 表示是否上市;第 4 位 0/1 表示当前时段是否接受订单申报。深证第 1 位:S 启动、O 开盘集合竞价、T 连续竞价、B 休市、C 收盘集合竞价、E 已闭市、H 临时停牌、A 盘后交易、V 波动性中断;第 2 位 0 正常、1 全天停牌。
def is_continuous_trading(market, status): # 沪深连续交易时段的判定逻辑不同,必须分市场 if market.startswith("USH"): return len(status) >= 1 and status[0] == "T" if market.startswith("USZ"): return len(status) >= 1 and status[0] == "T" return Falsemode_state是深市 2022.5.16 之后的新字段,格式是「交易方式+状态」成对拼接,比如1T2T3T4T5T,1 匹配成交、2 协商成交、3 点击成交、4 询价成交、5 竞买成交,状态字母含义同上。deal_mode表示最近一笔成交的成交方式。做深市策略时,如果你的因子只认竞价成交,就得用matching_volume、matching_turnover、matching_price这三个字段,而不是汇总后的volume,否则会把大宗协商的量混进你的封单模型里。
4.3 逐笔与委托队列的字段要点
逐笔成交和逐笔委托在 V2.0.7 之后都带了通道号字段,V2.1.9 逐笔委托又加了委托序号。委托序号是做「委托生命周期追踪」的关键:同一笔委托从下单到成交或撤单,靠序号串起来,才能算出真实的撤单率和挂单存活时间。委托队列给的是买一/卖一价位的排队明细,做封单前排识别时,队列长度和逐笔委托的匹配能看出有没有大单在队首反复插撤。
5. 避坑与排查:五个真实会卡住你的问题
5.1 现象:回调注册了但一条数据都不触发
原因通常有三个:一是订阅没成功,sub返回了负值但没检查;二是市场号填错,比如把USHA写成USHA(带空格)或用了不存在的市场号;三是极速盘口没开产品权限。解决:先检查sub/async_sub的返回码,再用get_tick_stock同步查一次同代码,确认账号本身有数据权限,最后核对市场号是否在手册的市场表里。
5.2 现象:盘中行情越来越慢,最后回调停了
原因就是 2.3 说的回调堵包,接收线程被业务逻辑占住。解决:回调里只入队,消费端异步处理;同时给队列设上限,满了记数告警而不是无限堆积。我一般还会在消费端打印recv_time - send_time的滑动分位数,延迟抬升时能第一时间定位是本地堵了还是链路问题。
5.3 现象:断线重连后订阅全没了
原因是m_connectsubscribe没设成 1,或者设了但用的是同步订阅且重连逻辑没走fucsubscribe回调。解决:set_env里显式设m_connectsubscribe=1,并注册fucsubscribe,在callback_type=2的回调里确认重发结果。另外func_notify收到code=3(重复登录被踢)时,SDK 已停止工作,必须手动重新login,自动重连救不了这种情况。
5.4 现象:级联模式下从 SDK 连不上主 SDK
级联模式要两步:主从 SDK 各自在程序同级目录建tool.ini,主 SDK 写master=true和socket_port=9999,从 SDK 写master=false和socket_port=9998;然后从 SDK 在login之前调用register_addr("master", 主IP, 9999)。常见错误是端口号后面没接换行符导致配置读取失败,以及从 SDK 先login再register_addr——顺序反了必然失败。还有一点,从 SDK 的账号密码必须和主 SDK 一致。
5.5 现象:离线调试模式不生效
start_offline_debug必须在实例创建之后、login之前调用,已经登录了再调不生效。数据文件名必须严格是stock.csv、index.csv、order.csv、queue.csv、trans.csv、superstock.csv、codelist.ini之一,命名不规范 SDK 直接不读。另外级联模式下只有父节点支持离线调试,子节点调了无效。
6. 用离线调试把策略回放跑通:一个可复现的验证套路
实盘前我最看重的一步,是用离线调试模式把整条数据链路跑一遍。它不连线上网关,login直接返回成功,SDK 按你指定的间隔和条数回放本地 CSV,触发和实时一模一样的回调。这意味着你可以在没有行情账号、没有开盘的时间段,把回调解析、因子计算、落库全流程验证一遍。
from datafeed.tds_api import TDS tds = TDS() def callback_stock(data_list, count, stats): # 回放时 stats 里的时间戳来自文件,不是真实链路时间 print("replay count:", count) tds.set_env(fnctickstock=callback_stock) # 关键:必须在 login 之前调用 tds.start_offline_debug( path="D:\\datafeed", # 数据目录 subtype=["stock", "trans"], # 只回放快照和逐笔成交 push_space=3000, # 每次推送间隔 3000ms push_count=5000, # 单次推送 5000 条 playback_times=1 # 文件遍历 1 次 ) # 离线模式下 login 不连服务器,直接成功 tds.login("test1", "123456")参数怎么调:push_space和push_count共同决定回放速度,push_count / push_space就是你的模拟吞吐。想压测消费端处理能力,就把push_count调大、push_space调小,看队列会不会满;想贴近真实盘中节奏,就按你实际每秒收到的条数量级来设。subtype只填你真正要验证的数据类型,填多了回放时间长且干扰排查。playback_times大于 1 可以用来验证长时间运行的稳定性,比如内存有没有缓慢增长。
有一个细节要注意:离线模式下查询接口只读文件前 3000 条(指数前 300 条)并去重后供查询,所以get_tick_stock在离线模式拿到的不是全量,别拿它验证全市场逻辑。回放触发回调的规则和实时一致——当次读取的数据里不含你订阅的代码,就不触发回调,这一点恰好能帮你验证订阅过滤逻辑写对没有。
我自己的习惯是:任何一次 SDK 升级、任何一次因子逻辑改动,都先用离线回放跑一遍,确认回调不炸、字段不空、队列不堵,再上实盘。这套流程帮我挡掉过好几次字段类型变更引发的静默错误——比如withdraw_buyturnover从 int 改成 float 那次,不做回放根本发现不了精度问题。希望帮到你。
本文还有配套的精品资源,点击获取