做量化分析这几年,我越来越觉得:股票数据API里最被低估的接口,就是当天逐笔交易数据。日线告诉你股价从哪涨到哪,分钟线告诉你涨跌的节奏,但真正决定一只股票明天是连板还是炸板的,往往藏在今天最后那几百笔逐笔成交里。这篇是这个系列的第03篇,专门聊怎么用股票数据接口api把某只股票某一天从开盘到收盘的每一笔成交拉下来,并且讲清楚字段含义、数据清洗、常见坑,以及如何从逐笔数据里算出一个能指导实盘的资金流信号。适合手里已经能拿到日线或分钟线、想把研究粒度往更细一层推进的量化新手和主观打板选手。
逐笔数据这个概念,光看名字很容易和分时成交混淆。简单说,分钟线是“按时间聚合的切片”,每根K线只是60秒内所有成交的统计结果;而逐笔数据是交易所撮合成交后吐出来的最小粒度记录——每一秒钟都可能有多笔成交,每一笔都有自己独立的成交价、成交量、成交方向和撮合时间。这是行情数据金字塔的塔尖。做高频、做日内T、做盘口异动监控的人,绕不开它;哪怕你做的是中低频,偶尔用它来复盘某一天的涨停板封板过程,也比单纯看分时图清晰十倍。
1. 逐笔交易数据到底是什么,做股票数据的人为什么绕不开它
1.1 逐笔成交与逐笔委托的区别:先分清楚这两样东西
刚开始接触逐笔数据的人,十有八九会在“成交”和“委托”之间犯迷糊。我们在行情软件里看到的“逐笔成交”,是交易所按照实际撮合成功的结果对外发布的记录,一条记录代表一笔已经成交的买卖,包含成交时间、成交价格、成交量,以及这笔成交是主动买还是主动卖。而“逐笔委托”是撤单之前的所有挂单行为,一条记录代表一次报单,包括申报价格和申报数量,但不代表一定会成交。
这两个数据源的用途完全不同。想分析主力资金的实际买入成本,看逐笔成交就够了;但如果你要做撤单率分析,或者研究“虚假封单”到底挂了多少又撤了多少,就必须看逐笔委托。很多第三方股票数据API平台会把这两个东西分开收费,因为数据量和维护成本不在一个量级。对绝大多数个人研究场景来说,先把逐笔成交吃透已经够你用很久了。
1.2 一分钟K线背后藏了多少细节:逐笔数据的不可替代性
我给你举一个特别直观的例子。某只股票在10:00到10:01这一分钟里成交了500万元,日线图上你只会看到这一分钟K线收了一根带长上影的阳线。但如果你把这一分钟拆成逐笔数据,会看到50笔小额卖出、3笔百万级的大单买入、以及价格在某个瞬间被一笔大单砸下去又迅速拉回来。同样是500万元成交,主力是在吸筹还是在出货,逐笔数据里体现得清清楚楚。
这就是逐笔数据不可替代的价值:它能帮你回答“价格为什么变”和“谁在推动价格变动”这两个问题。做波段的人可以依赖日线,但如果你做的是日内回转、涨停板接力、或者想要在收盘后验证“盘中那只大买单到底是真实资金还是对倒”,逐笔数据就是你手里唯一的照妖镜。也正因为如此,各大股票数据接口api平台几乎都把逐笔数据当成最核心的增值服务。
1.3 谁最需要当天逐笔数据,拿到手能用来干什么
按我的实际经验,真正高频使用当天逐笔数据的,大概有四类人:
- 量化研究员:做日内因子研究,比如计算“主买占比”“大单净流入”“开盘30分钟主动性买盘强度”等因子,这些指标全部依赖逐笔数据逐笔聚合。
- 短线打板客:复盘封板瞬间的单子结构,判断排板资金是真实抢筹还是虚假托单。
- 程序化交易者:盘中实时接收逐笔流,用队列模型判断盘口短暂失衡,做极短线的进出场。
- 数据工程师与算法工程师:为资金流监控、龙虎榜关联分析、异常交易行为识别等系统构建底层数据管道。
你不需要一开始就构思特别复杂的算法,先把你最关心的一只股票,某一天的逐笔数据拉下来,随便做点统计,你都会发现原来盘口背后藏着这么多肉眼看不到的规律。这篇博文就直接从数据获取讲起,把每一步怎么操作说透。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据源怎么选:从免费接口到专业终端的取舍
2.1 主流的股票逐笔数据获取渠道横向对比
市面上能拿到逐笔数据的渠道,大致可以分成三类:开源免费接口、半付费商业接口、专业行情终端。先说结论:没有任何一个渠道是完美且全免费的,因为逐笔数据本身就是交易所花钱买的,数据商不会做亏本买卖。
| 渠道类型 | 典型代表 | 逐笔数据可用性 | 数据总量限制 | 成本 |
|---|---|---|---|---|
| 开源免费库 | AkShare、Baostock | 部分有逐笔级快照,历史逐笔覆盖有限 | 单次拉取量受限制,容易限流 | 免费 |
| 商业数据接口 | Tushare Pro、聚宽、米筐 | 有专门逐笔字段,需积分或权限 | 按调取次数/流量计费,需要预约 | 低至中等 |
| 专业行情终端 | Wind、Choice、同花顺iFinD | 逐笔数据完整且规范 | 几乎不限,但接口封闭 | 昂贵 |
如果你是刚开始研究,我的建议是先用AkShare这类免费库跑通整个流程,确认自己的需求确实需要逐笔数据之后,再考虑开通商业接口。免费库最大的问题不是字段不够,而是稳定性和响应速度——盘中高峰期频繁拉取,非常容易被限流甚至封IP。做历史回测无所谓,做盘中实时监控就不够用了。
2.2 选择标准:先想清楚你是要历史还是要实时
不同场景对数据源的要求完全不同,这里一定要想明白自己的核心需求。
做历史复盘和因子研究,你需要的是一天的完整逐笔数据做离线分析,那么数据完整性和字段规范度排第一位,实时性根本不重要。收盘后慢慢拉都行。做盘中监控和实盘信号,你需要的是低延迟的逐笔流推送,那么普通HTTP轮询接口基本不合格,必须选支持WebSocket长连接或者高频轮询的专业股票数据API。
我自己是这么配置的:策略回测阶段用免费库或者商业接口的收盘后批量下载,把历史逐笔数据存到本地数据库;实盘阶段再用专业接口的实时流。两条路分开走,成本可控,也不会因为一个环节卡壳导致全盘崩溃。你别想着“一个接口搞定所有需求”,在逐笔数据这个领域,按需分级使用才是成熟做法。
2.3 权限门槛怎么看:积分、token、预约都是什么机制
商业平台普遍采用积分或权限管理,刚开始用会觉得繁琐,但背后有它的合理性。以那些需要累计积分的平台为例,基础日线接口可能1积分就能调,逐笔级别的接口可能需要2000积分以上,每一次拉取还消耗一定数量的积分。这个机制本质上是把昂贵的逐笔数据配额,分配给真正有持续需求的重度用户,同时拦住一次性薅数据的爬虫党。
实际使用中你只需要记住几件事:注册之后先完成实名认证,攒够基础积分;开通之前仔细查看接口文档里的“权限说明”,看清楚逐笔接口需要多少积分、单次最多可查多少天、每分钟最多调用多少次;如果数据量需求很大,很多平台支持“预约批量导出”,把任务提交给平台,处理完了再下载,比你自己一次性拉几千个文件舒服得多。这些细节文档里都有,但真的很多人没耐心看,结果调了半天全是权限错误,体验极差。
3. 当天逐笔交易数据的获取实操:以Tushare Pro为例
3.1 准备工作:安装依赖库、注册Token、检查权限
下面的操作示例以Tushare Pro为例,主要是因为它的逐笔数据字段规范、文档清晰,而且很多人在用。其他平台只是接口名不同,整体思路完全一致。
第一步,安装Python库并读取个人Token。登录平台后在个人主页找到Token字符串,这就是你调用股票数据API的钥匙。
python复制import tushare as ts
import pandas as pd
# 在官网的个人主页里找到你的token,替换成自己的
pro = ts.pro_api('你的token')
第二步,检查你的权限。推荐用“接口文档-积分要求”页面确认逐笔接口的权限状态,另外初始化之后可以快速拉一次空查询来验证。
python复制# 如果返回权限异常,说明积分不足
try:
df = pro.stock_tick(ts_code='000001.SZ', trade_date='20250220')
print(df.head())
except Exception as e:
print('权限或参数错误:', e)
这里有一个很容易被忽略的细节:不同版本的tushare库,接口支持程度不一样。建议把tushare升级到最新版本,否则你可能在旧版本里找不到逐笔接口。
bash复制pip install -U tushare
3.2 核心参数解析:股票代码、交易日、分页参数一个都不能错
拉取当天逐笔数据,最核心的三个参数就是:股票代码、交易日期、分页参数。看起来很基础,但每个都有坑。
先看股票代码。A股代码必须带后缀区分交易所,000001.SZ是平安银行,600000.SH是浦发银行。只写000001不带后缀,接口会直接报错。再看交易日期,格式必须是YYYYMMDD,也就是八位纯数字,用2025-02-20这种带横杠的格式,很多平台解析不了。最后是分页参数,这一条最坑。逐笔数据一天少则几万条,多则几十万条,接口单次最多返回几千行,所以必须循环翻页拉取,把所有分页结果拼接成完整DataFrame。
我当时第一次拉数据时没做翻页,拿到前2000条就以为数据是全的,还用这部分数据写了一版资金流因子,回测结果诡异得不行,最后发现原因出在数据不完整。后来学乖了,任何一次逐笔数据拉取都默认写循环翻页,宁可多拉几个空页,绝不放过任何一页。
python复制def fetch_tick_all(pro, ts_code, trade_date):
all_data = []
offset = 0
limit = 5000
while True:
df = pro.stock_tick(
ts_code=ts_code,
trade_date=trade_date,
limit=limit,
offset=offset
)
if df is None or df.empty:
break
all_data.append(df)
offset += limit
if len(df) < limit:
break
if not all_data:
return pd.DataFrame()
return pd.concat(all_data, ignore_index=True)
提示:不同平台接口名可能不一样,
stock_tick只是Tushare的命名方式。你如果用别的平台,就换成平台文档里的逐笔接口名,翻页逻辑大同小异。
3.3 逐笔数据字段逐个拆解:从成交时间到买卖订单编号
拿到数据之后不要急着算信号,先把字段看懂。不同平台字段名略有差异,但核心信息都围绕下面这张表:
| 字段含义 | 常见字段名 | 数据样例 | 说明 |
|---|---|---|---|
| 成交时间 | time / trade_time | 09:30:00.123 | 精确到毫秒的撮合时间 |
| 成交价 | price / trade_price | 10.25 | 该笔成交的撮合价格 |
| 成交量 | vol / trade_volume | 500 | 该笔成交的股数(注意不是手数) |
| 成交额 | amount | 5125.00 | 价格乘以数量,部分平台直接给出 |
| 主动买卖方向 | side / bs_flag | B / S | B代表主动买,S代表主动卖 |
| 成交编号 | trade_index | 1523661 | 交易所生成的流水号,唯一标识 |
| 买单编号 | bid_order | 881234 | 吃单方挂单编号,用于关联 |
| 卖单编号 | ask_order | 771245 | 被吃方挂单编号,用于关联 |
其中我最看重的是side / bs_flag这个字段,它代表了这笔成交是主动性买盘还是主动性卖盘。判断原则其实很简单:新进的买单如果与当前卖盘价格匹配,属于主动买,用B表示;新进的卖单如果与当前买盘价格匹配,属于主动卖,用S表示。主买主卖的累加差额,就是市场上常说的“资金净流入”的微观基础。
还有两个字段要提醒你们注意:第一,成交量单位是股不是手,1手等于100股,很多人在算金额时忘记乘100,算出来的成交额差两个数量级。第二,时间字段一般精确到毫秒,但有些平台会截断到秒,这对高频研究影响很大,买数据前先问清楚精度。
3.4 集合竞价和连续竞价的数据差异:别拿同一套逻辑套全天
A股一天的交易时段,并不是从头到尾都用同一种撮合规则。9:15到9:25是集合竞价阶段,其中9:20到9:25之间还不能撤单;9:30之后才是连续竞价。很多逐笔数据接口对竞价阶段的记录方式与连续竞价不一样,有的会把集合竞价成交合成一条总量记录,有的则完全舍弃竞价阶段的数据。
所以当你看到某只股票一天逐笔汇总的总成交量,比软件上显示的当日总成交量少一截,不必惊慌,很可能就是缺少了开盘集合竞价的量。做日内资金流统计时,我个人习惯单独把竞价阶段和大盘连续竞价阶段拆开处理,主买主卖这个指标本身不适用于集合竞价,因为竞价撮合没有传统意义上的主动吃单概念,计算出来的资金流没有参考意义。
4. 拿到原始逐笔数据后,怎么清洗和组织
4.1 时间戳解析、交易时段过滤必须放在第一步
接口返回的时间字段,不同平台格式五花八门。有的是'2025-02-20 09:30:00.123'这种完整字符串,有的直接给你一个Unix毫秒时间戳,还有的是'09:30:00.123'这种纯时间。数据清洗第一步,就是把它们统一转成方便计算的datetime格式。
python复制# 假设原始列叫time_str,格式为 09:30:00.123
df['dt'] = pd.to_datetime(df['time_str'], format='%H:%M:%S.%f')
df['trade_date'] = pd.to_datetime('2025-02-20')
df['ts'] = df['trade_date'] + df['dt'].dt.time.astype('timedelta64[ns]')
时间统一之后,紧接着做交易日时段过滤。正常连续竞价时段是上午9:30到11:30、下午13:00到15:00。有些接口会把盘前盘后的零散数据也推给你,或者把集合竞价片段混进来,这时候按时间范围过滤一次,能排除掉大量脏数据。
python复制df = df[
((df['ts'].dt.time >= pd.to_datetime('09:30:00').time()) &
(df['ts'].dt.time <= pd.to_datetime('11:30:00').time())) |
((df['ts'].dt.time >= pd.to_datetime('13:00:00').time()) &
(df['ts'].dt.time <= pd.to_datetime('15:00:00').time()))
]
如果你做的是数据管道的批量处理,建议把这一步封装成独立函数,因为几乎每一个后续分析场景都要复用。
4.2 主动买卖方向判定与大单阈值设置
上一节说过side / bs_flag字段直接给出了主买主卖方向,但有些数据源不提供这个字段,只提供成交价和买卖订单编号。你需要自己判断方向,方法也不难:如果一笔成交的成交价高于或等于当时买一价,倾向于判定为主动买;如果低于或等于卖一价,倾向于主动卖。判断时还需要当时的盘口快照,所以没有盘口数据时会比较麻烦。这也是我优先推荐直接买带方向字段的数据源的原因——省下的时间远超差价。
拿到方向字段之后,另一个关键设定是大单阈值。不同体量的股票,大单的标准完全不同。茅台的一天成交额几十亿,500万才算大单;一只日成交5000万的小盘股,100万已经是很重的单子了。所以不要用一个固定金额硬套所有股票,我常用的做法是:先算出该股过去20个交易日平均单笔成交额,把当前单笔成交额超过平均单笔金额5倍的定义为大单,再把超过50倍的认定为超大单。这样阈值随个股流动性自适应,比拍脑袋定100万要靠谱得多。
python复制avg_amount = df['amount'].mean()
df['large_flag'] = df['amount'] > avg_amount * 5
df['super_large_flag'] = df['amount'] > avg_amount * 50
4.3 数据落地:别把逐笔数据存在CSV里反复读
逐笔数据的量级一张CSV根本扛不住。一只常态成交的股票,一天下来几万条记录很常见,如果每天存一个CSV,一个月就是几十个文件,回测时要跨文件聚合,IO开销大到让人崩溃。我建议从第一天就把数据落到数据库里,MySQL和PostgreSQL都能用,但更推荐ClickHouse或DuckDB这类列式存储方案。DuckDB尤其适合个人研究,单机运行、无需服务端、直接读Parquet文件,查询速度吊打Pandas硬算。
我自己个人项目的存储方案是:按股票代码和交易日期分区的Parquet文件,再搭配DuckDB做查询聚合。每天收盘后自动拉取当天数据,写入当天分区,回测时用SQL直接跨大量日期做聚合,速度快并且代码量也少。这一套架构做到后面会非常省心。
sql复制-- DuckDB示例:计算某只股票某个月每日主买金额
SELECT
trade_date,
SUM(CASE WHEN side = 'B' THEN amount ELSE 0 END) AS buy_amount,
SUM(CASE WHEN side = 'S' THEN amount ELSE 0 END) AS sell_amount
FROM read_parquet('data/000001.SZ/*.parquet')
GROUP BY trade_date
ORDER BY trade_date;
5. 实操中最容易踩的5个坑(常见问题排查实录)
5.1 问题一:接口返回空数据,第一反应应该是查日期有没有复权干扰
逐笔数据本身不存在复权概念,因为它是盘口当时的真实成交价,不需要处理除权除息。所以当你拉取某一天的数据是空的,不要先怀疑自己代码写错了,先查一下当天是不是周末、节假日或者股票停牌。再有就是,权证、退市整理期、新股上市首日的行情记录规则也可能与常规股票不同,接口未收录实属正常。
排查空数据最快的办法,是拿同一天另一个数据源对比验证,比如日线接口。如果日线接口有数据而逐笔接口没有,多半是逐笔数据权限或覆盖范围的问题,及时联系数据商客服比你自己盲猜高效得多。有一次我研究某只次新股,发现上市前5天拉不到逐笔数据,后来才知道平台对次新股上市初期的数据收录有延迟,过两周再拉就全出来了。
5.2 问题二:数据量太大导致接口超时,分页数量和本地批量要配合
一只超级大盘股一天的逐笔记录能到30万行以上,如果单次接口限5000条,你需要拉60次才能拿全。频繁请求不仅耗时,还容易触发限流。解决办法有两个方向。第一是充分利用接口的批量能力,很多平台的逐笔接口支持一次请求指定多只股票,或者直接按整个交易日来批量导出,一天的数据一个文件搞定。第二是本地分批落库,每拉到几千条就写入数据库,不要全部攒在内存里。
我踩过的坑就是一口气把所有分页结果拼成一个大DataFrame再入库,结果几十万行数据直接耗尽内存,脚本崩溃在最后一步,前功尽弃。后来改成边拉边写,再也没有出现过内存爆炸的问题。
python复制# 边拉边存示例
for offset in range(0, 500000, 5000):
df_page = pro.stock_tick(ts_code='600000.SH', trade_date='20250220',
limit=5000, offset=offset)
if df_page is None or df_page.empty:
break
df_page.to_parquet(f'./tick_temp/offset_{offset}.parquet')
5.3 问题三:价格字段突然出现0.00或者成交价格离谱
正常情况下逐笔成交价不应该出现0。如果遇到0,多半是接口偶尔返回的异常占位记录,或者盘口瞬间停牌恢复时的系统异常单。处理方式是直接过滤掉price<=0的记录,这种数据量占比极低,剔除后对统计结果毫无影响。
另外一种离谱情况是成交价明显偏离当日涨跌停价格区间,比如一只跌停价10元的股票,逐笔里冒出一笔成交价9.8元。这种八成是数据源跨市场串包或者解析错位,排查思路是先看看成交时间前后的记录是否连续,再和分时数据对比,确认异常后把这一小段数据剔除。千万别把这当成什么“漏网之鱼”的事件驱动机会,数据源脏的概率远大于市场真的按错误价格成交。
5.4 问题四:上证和深证的字段格式不一致
上交所和深交所的逐笔数据发布规则本来就不同,导致很多平台接口的字段细节也不一致。比如某些平台对主动买卖方向字段,上交所股票返回B/S,深交所股票返回2/4或者buy/sell。字段命名、时间精度、单笔成交量单位都可能存在差异。如果你同时研究沪深两市的股票,一定要在代码里做交易所适配,不要想当然认为两边的字段完全一样。
我最开始写数据清洗函数时只按上交所的规则处理,结果深交所所有记录的方向字段全是空值,后来逐字段核对了接口文档才发现问题。从那以后,我清洗脚本的第一步永远是先判断股票代码后缀,再走对应的清洗逻辑。
| 交易所 | 方向字段示例 | 时间精度 | 成交量单位 |
|---|---|---|---|
| 上交所 | B / S | 毫秒级 | 股 |
| 深交所 | buy / sell 或 数字枚举 | 毫秒级 | 股 |
5.5 问题五:盘中数据拉二三次结果不一样
盘中实时拉逐笔数据,每次都只能看到“截至当前时刻”的增量数据;收盘后再拉,数据才是完整的。这导致你在盘中统计的主力净流入,和收盘后统计的数字往往差很多,容易让人对自己的策略产生怀疑。这不是接口错了,而是逐笔数据天然就是增量更新的。
解决方法是给每次拉取的数据加上一个快照时间戳,盘中统计时只做“暂定信号”,收盘后用全量数据重新确认。对于做中低频的人,我甚至建议只看收盘后的逐笔数据,盘中用实时数据折腾自己纯属自找麻烦。
6. 从逐笔数据到可用的策略信号:一个简单的资金流计算案例
6.1 用主买主卖差额计算这一天的真实多空力量
把逐笔数据清洗干净之后,第一个最值得算的指标就是主买主卖净额。这个概念翻译成白话就是:今天所有主动出高价买股票的钱,减去所有主动压价卖股票的钱,差额为正说明主动性买盘更强,差额为负说明主动性卖盘更强。
在Python里实现这个逻辑非常简单,但实际应用时,我最建议关注的是尾盘最后30分钟的主买占比。因为A股当天收盘前的多空对决,往往决定了第二天集合竞价的市场预期。尾盘30分钟主买占比明显放大的股票,次日高开的概率会显著提升,这是我复盘了大量涨停板之后总结出的规律,大家可以用自己的逐笔数据验证一下。
python复制# 计算某一天的分时段主买净额
df['period'] = 'morning'
df.loc[df['ts'] >= '2025-02-20 14:30:00', 'period'] = 'tail'
result = df.groupby('period').apply(
lambda x: (x.loc[x['side'] == 'B', 'amount'].sum() -
x.loc[x['side'] == 'S', 'amount'].sum())
)
print(result)
6.2 大单方向与价格变动的配合:识别真正的拉升意图
只有主买主卖总额还不够,因为大单的方向往往比总量更能说明问题。实际操作中,我是这样设计逻辑的:先定义大单阈值,然后计算大单主动买入金额占比,最后结合同时间段的股价涨幅来判断拉升的“质量”。
举个例子:某只股票全天上涨3%,同时大单主买金额占比超过60%,说明上涨主要由大资金主动推动,这种上涨的持续性通常较好,后续回调时也更容易出现承接。反过来,如果股价上涨3%,但大单主卖金额大于主买金额,说明上涨是靠中小单堆上去的,大资金正在借机出货。这种背离信号,不看逐笔数据根本发现不了,日线级别完全无法感知。
6.3 逐笔数据与分钟线、日线数据的联动:一句话总结我的用法
最后分享一个我在实际盘面中反复验证过的联动思路:用日线选股,用分钟线择时,用逐笔数据做最终验证。具体操作是,先靠日线找到近期有明显放量和强势形态的股票,再用分钟线定位出日内关键支撑位,最后打开该股当天的逐笔数据,确认支撑位附近是否有连续的大单主动买入托盘。三者逻辑一致时再动手,胜率就高很多。
我自己之前吃过一次亏,一只股票盘口看着有大单在买,分时线也在拉升,按日线形态果断追进去,结果当天就被砸了。收盘后复盘逐笔数据才发现,盘口那几笔大单是反复挂撤制造出来的假象,真正的统计结果是大单净流出。从那以后,逐笔数据成了我复盘流程里必不可少的一环,宁可慢一步,也要等数据说话。逐笔数据不会骗人,但你的盘感和情绪会,这就是数据研究最实在的一点价值。
