1. 课程导入与数据认知
在逐笔(Tick)级别的市场数据里,每一笔成交都带有时间戳、价格和成交量。把这些成交按时间排成一列,相邻两笔之间就存在一个时间差——这就是逐笔成交间隔(inter-trade duration)。它看起来只是一个简单的时间减法,却是刻画市场微观结构最直接的一把尺子:间隔短,说明成交密集、买卖双方撮合频繁;间隔长,说明市场清淡、流动性稀薄。本课程要做的,就是把这条"时间轴上的稀疏与密集"转化为可计算、可比较、可解释的交易活跃度指标。
一、逐笔成交间隔的定义与口径
设第 i 笔成交的时间戳为 ti,则第 i 笔成交的间隔定义为 di = ti − ti−1,单位通常取毫秒或秒。定义本身简单,但口径必须先讲清楚,否则后续所有指标都会失真:
- 时间戳精度:交易所快照或逐笔数据的时间精度可能是毫秒、10 毫秒甚至秒级。精度越粗,同一时间戳内会出现多笔成交,间隔为 0 的情况大量出现,需要决定是合并还是保留。
- 连续竞价与集合竞价:集合竞价阶段(如开盘、收盘)成交集中在瞬间,间隔接近 0,与连续竞价的分布完全不同,通常应剔除或单独处理。
- 午间休市与隔夜:跨休市、跨交易日的相邻两笔成交,其时间差包含非交易时段,必须剔除,否则会制造出巨大的伪间隔。
- 停牌与异常笔:长时间停牌后的首笔成交、撤单引发的异常记录,需要按规则过滤。
因此,一个可用的间隔序列,是在"同一交易时段、同一标的、剔除集合竞价与休市"的前提下,对相邻成交时间戳做差得到的。这一步做扎实,后面的统计才有意义。
二、交易活跃度指标的意义
成交间隔的倒数,即单位时间内的成交笔数,就是最朴素的成交频率。围绕间隔序列,可以构造一组互相补充的活跃度指标:平均间隔反映整体节奏;间隔的中位数比均值更抗极端值;间隔的变异系数(标准差/均值)刻画节奏的稳定性;而"间隔超过某阈值(如 5 秒)的成交占比"则直接度量市场的"冷场"程度。此外,单位时间成交量、平均每笔成交量、买卖方向交替频率等,也都是活跃度的侧面。
这些指标的价值在于:它们把"今天这只股票活跃不活跃"这种模糊判断,变成了可横向比较(不同标的)、可纵向比较(不同时段)、可回测验证的数值。在流动性评估、交易成本估计、做市策略触发、异常交易监测中,间隔类指标都是基础输入。
三、课程整体学习路径
本课程共 30 章,围绕"数据—指标—应用"三层递进展开。第一层是数据认知与口径(本章及后续几章),解决"数据长什么样、间隔怎么算、坑在哪里";第二层是指标构造与统计(中段章节),依次覆盖平均间隔、中位间隔、间隔分布、成交频率、变异系数、冷场占比、分时段与分标的对比等,并给出计算方式与示例参数;第三层是应用与实战(后段章节),把指标用于流动性评估、策略信号、异常监测与回测验证,并讨论指标之间的相关性与局限。学习路径不是线性背诵,而是"先能算、再会看、最后能用"。
四、实战数据集介绍
为了让每章都有可操作的对象,课程配套一份示例数据集(以下字段与规模为教学设定,非真实行情实测)。数据集包含某只标的连续 5 个交易日的逐笔成交记录,字段为:trade_id(成交编号)、timestamp(毫秒级时间戳)、price(成交价)、volume(成交量,股)、side(主动买/主动卖)。每日约 8000~15000 笔,覆盖上午 9:30–11:30 与下午 13:00–15:00 两个连续竞价时段。
基于该数据集,本章先做一个最小可运行的间隔计算示例,帮助建立手感。以下为 Python 伪代码,展示口径处理的关键步骤:
import pandas as pd
df = pd.read_csv("sample_ticks.csv", parse_dates=["timestamp"])
df = df.sort_values("timestamp").reset_index(drop=True)
# 1) 只保留连续竞价时段
def in_session(ts):
t = ts.time()
return (pd.Timestamp("09:30").time() <= t <= pd.Timestamp("11:30").time()) or \
(pd.Timestamp("13:00").time() <= t <= pd.Timestamp("15:00").time())
df = df[df["timestamp"].apply(in_session)]
# 2) 按交易日分组,组内做差,避免跨日伪间隔
df["date"] = df["timestamp"].dt.date
df["gap_ms"] = df.groupby("date")["timestamp"].diff().dt.total_seconds() * 1000
# 3) 剔除首笔(diff 为 NaN)与异常负值
gaps = df["gap_ms"].dropna()
gaps = gaps[gaps >= 0]
print("平均间隔(ms):", gaps.mean())
print("中位间隔(ms):", gaps.median())
print("冷场占比(>5000ms):", (gaps > 5000).mean())
在一份教学示例数据上,上述代码可能得到类似"平均间隔约 1200 毫秒、中位间隔约 600 毫秒、超过 5 秒的间隔占比约 8%"的结果(数值为示例,用于说明观察方式)。注意均值明显大于中位数,说明间隔分布右偏——少数长间隔拉高了均值,这正是后续章节要重点讨论的分布特征。判断活跃度时,应同时看均值、中位数与冷场占比,而不是只看一个数。