Tick 数据适合观察比 K 线更细的行情变化,例如最新成交价、累计成交量、持仓量和盘口快照如何更新。它并不自动等于交易所逐笔成交明细,也不能单靠一条 Tick 序列完整还原撮合队列。使用 TqSdk 获取 Tick 时,先明确自己要研究的是短周期变化、成交量增量还是盘口状态,再决定数据长度和处理频率,远比"数据越细越好"更重要。
获取 Tick 序列并等待更新
get_tick_serial 返回一段动态序列,使用方式与 K 线相似:先订阅,再由 wait_update 推进数据,最后判断末行是否变化。下面示例只打印新的 Tick 时间、最新价和累计成交量。
import os
from tqsdk import TqApi, TqAuth
api = TqApi(
auth=TqAuth(os.environ["TQ_USER"], os.environ["TQ_PASSWORD"])
)
ticks = api.get_tick_serial("KQ.m@SHFE.rb", data_length=200)
try:
while True:
api.wait_update()
if api.is_changing(ticks.iloc[-1], "datetime"):
latest = ticks.iloc[-1]
print(latest.datetime, latest.last_price, latest.volume)
finally:
api.close()
末行会随着新数据到达而变化,序列长度达到上限后,旧行会被滚动移出。data_length 决定内存中保留多少条,不代表服务器从此永久保存这些数据。如果研究需要跨天或长期复盘,应设计明确的落盘过程,并处理交易日、合约和时间字段,而不是依赖运行中的动态对象一直保留历史。
Tick 字段该怎样理解
Tick 中常见的字段包括时间、最新价、最高价、最低价、累计成交量、成交额、持仓量,以及买卖盘口价格和数量。盘口字段表示该时刻可见的行情快照,累计字段通常需要与上一条有效记录做差,才能得到这次更新对应的增量。
例如,想观察成交量是否增加,不能只看 volume 是否大于零,因为交易日开始后它通常一直是正数。应比较相邻 Tick 的累计成交量:
if len(ticks) >= 2:
previous = ticks.iloc[-2]
current = ticks.iloc[-1]
volume_delta = current.volume - previous.volume
if volume_delta > 0:
print("本次更新的成交量增量:", volume_delta)
跨交易日、数据缺失或序列初始阶段可能让简单差分失去意义,因此正式程序还需要识别交易日边界并检查数值有效性。示例的作用是说明累计值和增量值不是同一概念,不应直接把它当成完整的成交归因算法。
时间字段同样需要先转换和校验。Tick 更新频率高,若保存时只保留格式化到秒的字符串,多条记录可能失去原有顺序。长期落盘应保存足够精度的时间、合约代码和必要字段,同时记录数据产生的交易日。展示层可以再转换为易读时间,原始定位信息不要在保存前丢掉。
初始序列中的无效值不应直接参与差分。至少确认当前行与上一行的时间有效、累计字段不是空值,并且顺序连续。出现负增量时,先检查是否跨日或数据窗口发生变化,不要立刻解释为"反向成交"。
哪些问题适合用 Tick 回答
第一类是短周期行情观察。若一分钟 K 线过于粗糙,Tick 可以帮助查看价格在这一分钟内部怎样变化,盘口是否频繁移动,以及成交量和持仓量何时出现明显增量。
第二类是信号触发时机验证。某些策略使用 K 线形成方向判断,但希望在更细的行情更新上检查入场条件。此时可以让 K 线负责较慢的背景判断,让 Tick 负责较快的触发,但必须明确两个节奏如何衔接,避免每条 Tick 都重复提交同一个交易意图。
第三类是运行诊断。程序声称"没有行情"时,观察 Tick 的时间和累计量是否变化,可以区分订阅没有成功、当前没有新成交,还是业务代码没有正确响应更新。诊断代码最好与交易动作隔离,先证明数据链路,再检查策略逻辑。
Tick 不能替代什么
Tick 不是"所有真实成交细节"的同义词。行情快照受到数据源定义和推送方式影响,一次更新可能汇总了多个市场变化;只有买卖盘和最新价,也不足以准确推断每一笔成交的主动方向。若研究问题依赖完整逐笔顺序、排队位置或交易所撮合细节,需要确认数据类型是否真的支持,而不能从普通 Tick 快照中强行推导。
Tick 也不能替代交易回报。行情显示某个价格出现过,不代表自己的委托一定在该价格成交。订单是否成交要看委托对象和成交记录,受报单时间、队列和实际撮合影响。用行情价格直接填充"策略成交价",会让回测或实时统计产生过于乐观的结果。
数据更细还会带来计算和存储压力。如果策略本身只在每根分钟线结束时决策,持续保存和处理所有 Tick 可能没有实际收益。先由决策规则确定所需粒度,再选择数据,能避免系统复杂度无端上升。
回测或复盘时也要确认实时 Tick 与历史数据的定义是否一致。实时程序观察到的更新节奏,不一定能在另一种数据源中逐条复现。若策略依赖"每次更新都执行一次"的细节,应分别验证历史环境和实时环境,而不是只看最终价格序列大致相同。
盘口字段容易诱发过度解释。某一档买量增加,只说明快照中的可见数量变化,不能单独证明参与者的真实意图;挂单还可能撤销。把盘口变化作为研究变量没有问题,但应通过明确统计和样本验证,不把单次变化直接写成确定的交易结论。
与 K 线组合时怎样避免重复触发
可以分别维护两个变化入口:K 线 datetime 变化时更新背景信号,Tick datetime 变化时检查细粒度条件。背景信号应保存为明确状态,只有状态变化或满足新的触发条件时才产生动作。
direction = 0
while True:
api.wait_update()
if api.is_changing(klines.iloc[-1], "datetime"):
direction = calculate_direction(klines.iloc[:-1])
if api.is_changing(ticks.iloc[-1], "datetime"):
if direction != 0:
check_entry_once(direction, ticks.iloc[-1])
这里的两个函数只是结构占位,正式实现必须补上防重复、持仓和委托状态判断。重点是让慢信号和快触发分别有自己的事件来源,而不是把所有计算塞进每一条 Tick 更新中。
防重复不能只靠时间间隔。例如,同一方向条件连续满足时,固定等待一秒后仍可能再次触发。更可靠的方法是记录信号状态、目标持仓、当前活动委托和上一次处理的事件标识,只有业务状态真正允许时才产生新动作。行情触发负责唤醒判断,不负责替你决定订单是否应该再次出现。
保存数据也应与策略循环解耦到清楚的职责层。实时分支只提取需要落盘的字段,写入层负责批量保存和异常处理;若保存失败,应有可见日志,但不能让未处理的异常悄悄终止整个行情循环。数据完整性要求较高时,还需设计重启后的缺口检查。
Tick 使用清单
- 先定义问题需要 Tick 还是 K 线,不因数据更细就默认更好。
- 对累计成交量等字段使用差分时处理初始值和交易日边界。
- 行情快照、逐笔明细和自己的成交回报严格区分。
- 监听真实变化并设置防重复状态,不让每条 Tick 重复发出动作。
- 长期研究显式保存所需数据,同时控制长度、存储量和异常值。
Tick 的价值在于把观察尺度向市场更新靠近,而不是让所有结论天然更准确。只要清楚数据表达什么、没有表达什么,并让处理频率服务于策略规则,它才会成为有效的研究工具,而不是新的噪声来源。