2. 分钟聚合核心指标定义
逐笔行情是交易所按每一笔成交推送的原始数据流,一条记录通常包含成交时间、成交价、成交量、成交额、买卖方向等字段。分钟聚合的任务,是把这一分钟内所有逐笔成交压缩成一根K线,并附带若干统计量。指标口径一旦定义不清,后续的信号计算、回测和风控都会建立在错误的基础上。本节围绕一根分钟K线需要产出的核心字段,逐一说明计算口径与业务含义。
一、OHLC:价格区间的四个端点
OHLC分别指开盘价(Open)、最高价(High)、最低价(Low)和收盘价(Close)。在逐笔聚合场景下,它们的口径需要明确:
- 开盘价:该分钟窗口内第一笔成交的成交价,而不是窗口起始时刻的快照价。若窗口内无成交,则沿用上一分钟收盘价,并标记为“无成交填充”。
- 最高价 / 最低价:窗口内所有逐笔成交价的最大值与最小值,只统计实际成交,不包含盘口挂单价。
- 收盘价:窗口内最后一笔成交的成交价,是下一分钟开盘价填充的参照。
业务含义上,OHLC刻画了价格在该分钟内的波动区间与起止位置。开盘价与收盘价之差反映净方向,最高价与最低价之差反映波动幅度。需要注意,若某分钟只有一笔成交,则O=H=L=C,这是正常现象而非数据异常,聚合逻辑不应将其丢弃。
二、成交量、成交额与笔数
成交量是该分钟窗口内所有逐笔成交的成交量之和,单位需与数据源保持一致(股、手或张),聚合过程中不得混用。成交额是逐笔成交额之和,也可用“成交价×成交量”逐笔累加得到,但更推荐直接累加原始成交额字段,以避免价格精度带来的舍入误差。
笔数是窗口内成交记录条数,反映交易活跃度。笔数与成交量结合可以观察平均单笔规模:平均单笔量偏大往往意味着大资金参与,笔数多而单笔量小则更接近散户主导的活跃行情。这三个字段都是可加量,跨分钟汇总时直接求和即可,这为后续的小时、日线聚合提供了便利。
三、均价:两种口径要分清
均价常见两种口径,业务含义不同,不能混用:
- 成交量加权均价(VWAP):成交额 ÷ 成交量,反映该分钟内资金实际成交的平均成本,是衡量成交质量的核心指标。
- 算术均价:(最高价 + 最低价) ÷ 2 或四价均值,仅作为价格中枢的粗略参考,不反映资金成本。
实战中应以VWAP为主。当收盘价明显高于VWAP时,说明该分钟内的成交更多集中在价格低位、尾段拉升,买方力量偏强;反之则偏弱。若成交量为零,VWAP无定义,应置空而不是填零,避免污染后续计算。
四、买卖方向统计
逐笔数据通常带有买卖方向标记(主动买或主动卖)。按方向分别累加成交量,可得到主动买量和主动卖量,二者之差即净主动买入量,是判断资金流向的直接依据。若数据源未提供方向字段,可用逐笔成交价与上一笔成交价比较来推断:价格上行视为主动买,下行视为主动卖,价格不变则沿用上一笔方向或归为中性。
五、聚合流程与字段关系
下面用一张图展示逐笔记录进入分钟窗口后,各指标字段是如何被计算出来的,以及它们之间的依赖关系。
六、一个可对照的示例
假设某分钟窗口内共有5笔成交,数据如下(示例数据,仅用于说明计算过程):
| 序号 | 成交价 | 成交量 | 成交额 | 方向 |
|---|---|---|---|---|
| 1 | 10.00 | 200 | 2000 | 买 |
| 2 | 10.05 | 300 | 3015 | 买 |
| 3 | 9.98 | 100 | 998 | 卖 |
| 4 | 10.02 | 400 | 4008 | 买 |
| 5 | 10.01 | 150 | 1501.5 | 卖 |
按前述口径计算:Open=10.00,High=10.05,Low=9.98,Close=10.01;成交量=200+300+100+400+150=1150;成交额=2000+3015+998+4008+1501.5=11522.5;笔数=5;VWAP=11522.5÷1150≈10.0196;主动买量=200+300+400=900,主动卖量=100+150=250,净主动买入=650。收盘价10.01略低于VWAP,说明该分钟尾段成交价格略低于资金平均成本,买方优势有所收敛。
七、实现时的关键细节
在代码实现上,建议为每个分钟窗口维护一个聚合状态对象,逐笔更新而非先缓存全部记录再计算,这样内存占用与窗口内笔数无关。更新逻辑可概括为:首笔写入Open,每笔更新High/Low,每笔覆盖Close,量额笔数累加,按方向分流累加。窗口切换时输出结果并重置状态。
state = {open:None, high:-inf, low:inf, close:None,
vol:0, amt:0, cnt:0, buyVol:0, sellVol:0}
for tick in window:
if state.open is None: state.open = tick.price
state.high = max(state.high, tick.price)
state.low = min(state.low, tick.price)
state.close = tick.price
state.vol += tick.vol; state.amt += tick.amt; state.cnt += 1
if tick.side == 'B': state.buyVol += tick.vol
else: state.sellVol += tick.vol
vwap = state.amt / state.vol if state.vol else None
最后强调两点业务判断:其一,指标之间要能相互印证,例如成交额与“价×量”的偏差过大,往往提示数据源字段异常;其二,无成交分钟的填充规则必须在整条数据链上保持一致,否则跨周期聚合时会出现价格跳变。把口径写进文档、写进代码注释,并在回测与实盘使用同一套聚合函数,是保证分钟指标可信的最低要求。