TqSdk K 线怎么读?字段、更新判断和新 K 线识别
在 TqSdk 中,K 线不是每次调用都返回一份全新结果,而是一段会被事件循环持续更新的序列。正确的使用方式是先订阅所需周期和长度,再通过wait_update接收变化,最后用is_changing区分“当前 K 线价格变了”和“已经生成一根新 K 线”。这一区分直接决定指标会不会重复计算、信号会不会重复触发。
获取一段可更新的 K 线序列
get_kline_serial的核心参数是合约代码、周期秒数和数据长度。下面订阅螺纹钢主连的 1 分钟 K 线,并保留最近 100 根。主连适合做连续研究示例;涉及交易时,还需要映射到实际月份合约。
import os from tqsdk import TqApi, TqAuth api = TqApi( auth=TqAuth(os.environ["TQ_USER"], os.environ["TQ_PASSWORD"]) ) klines = api.get_kline_serial( "KQ.m@SHFE.rb", duration_seconds=60, data_length=100, ) try: while True: api.wait_update() if api.is_changing(klines.iloc[-1], "close"): print(klines.iloc[-1][["datetime", "open", "high", "low", "close"]]) finally: api.close()返回值具有 DataFrame 风格,可以使用iloc、列选择和常见 pandas 计算方式。不过它不是静态表格:事件循环收到数据后,末行会继续变化,出现新周期时整段序列也会向前滚动。保存某一时刻的数据时,应显式复制需要的内容,而不是假设这个对象永远不变。
常用字段分别表达什么
每根 K 线包含起始时间、开盘价、最高价、最低价、收盘价、成交量和持仓量等字段。datetime是纳秒级时间戳,表示该根 K 线的起始时间。显示给人阅读时可以转换为日期时间;程序判断新 K 线时,直接比较或监听这个字段更稳定。
open、high、low、close描述该周期内的价格范围,volume是成交量,close_oi是周期结束时的持仓量。不同字段解决的问题不同:计算均线通常使用收盘价,观察周期内波动会用到最高价和最低价,研究量价关系还需要成交量和持仓量。不要因为表里字段很多,就一次性把所有字段都加入策略。
订阅刚建立时,前部数据可能存在空值,尤其是可用历史不足或对象尚未完成首次更新时。计算前应检查所需窗口是否已经具备有效数据。把空值直接填成零,可能会人为制造价格跳变和错误信号,因此应先明确缺失的原因。
时间字段还承担对齐作用。两个合约或两个周期的序列长度相同,不代表最后一行对应同一时刻。跨品种比较之前,应先检查datetime是否能够对齐;遇到上市时间不同或交易时段差异时,直接按行号相减可能把不同时间的数据拼在一起。研究结果一旦依赖错位数据,后面的公式再精确也没有意义。
序列滚动意味着行位置表达的是“当前距离末尾多远”,而不是某根 K 线永久不变的身份。需要长期保存事件时,应记录合约、周期和时间戳,把它们作为回查依据。只记iloc[-2]这样的相对位置,程序下一轮更新后就无法说明当时引用的是哪根线。
当前 K 线变化不等于新 K 线
在一分钟尚未结束时,最新成交会不断改变当前 K 线的最高、最低、收盘和成交量。监听close变化适合做盘中观察,却可能在一分钟内触发许多次。如果策略定义为“每根一分钟线结束后计算一次”,应该监听末行datetime的变化。
last_time = None while True: api.wait_update() current_time = klines.iloc[-1].datetime if last_time is None: last_time = current_time continue if api.is_changing(klines.iloc[-1], "datetime"): closed_bar = klines.iloc[-2] print("上一根已结束 K 线收盘价:", closed_bar.close) last_time = current_time当最后一行的时间变化时,原来的末行已经移动到倒数第二行,所以已完成周期通常读取iloc[-2]。首次进入循环时还没有“上一轮”可比较,代码需要跳过初始化,避免把订阅完成误判成策略事件。
指标计算要与触发频率一致
以简单均线为例,如果策略只在新 K 线出现时决策,就在新线事件中计算已完成 K 线的窗口。这样同一根 K 线不会因为盘中价格变化重复产生信号,也便于回测和实时运行保持相同节奏。
if api.is_changing(klines.iloc[-1], "datetime"): closed = klines.close.iloc[:-1] ma5 = closed.iloc[-5:].mean() ma20 = closed.iloc[-20:].mean() print("MA5:", ma5, "MA20:", ma20)在计算前还应确认至少有 20 根有效收盘价。示例只展示数据与触发关系,并不是完整策略:均线交叉如何定义、是否过滤重复信号、当前持仓怎样处理,都需要单独写成明确规则。
多个周期同时使用时,每个序列都有自己的更新时间。不要因为较短周期更新,就假设较长周期也已经形成新线。分别监听各自的datetime,再决定是否组合计算,逻辑会更清楚。
多合约策略也适用同样原则。每个订阅对象只在自身发生相关变化时更新对应指标,再把最新结果写入一个清楚的状态表。最终组合判断读取这个状态表,并检查各项时间是否仍然有效。这样能避免任意一个合约更新时,把其他合约的旧数据误当作同一时刻的新信息。
计算函数最好接收一份明确的数据切片并返回结果,不在函数内部创建新的 API 或事件循环。行情推进和数学计算分开后,同一个指标函数既能用历史样本测试,也能被实时循环调用,更容易发现窗口长度、空值和边界索引问题。
K 线研究的边界
K 线把一个周期内的交易压缩为几个汇总字段,它适合趋势、波动和指标研究,但不能还原周期内每一笔成交的先后顺序。仅凭一分钟 K 线无法知道最高价和最低价哪个先出现,也无法精确复刻所有成交队列。
回测结果同样受数据粒度和模拟撮合规则影响。策略若依赖非常细的盘中顺序,应使用更合适的数据并明确验证边界。主连序列用于研究连续走势时,还要注意它不是一个可以直接成交的具体合约,实盘执行需要处理实际合约和换月。
还要避免未来数据泄漏。做实时判断时,只能使用当时已经形成的数据;在新 K 线刚出现时,上一根已完成线可以使用,当前末行仍在形成。若在历史表上一次性计算指标后再挑选结果,需要确认公式没有通过居中窗口、反向填充或后续行间接使用未来信息。
验证 K 线代码时,可以手工记录连续几次事件的末行时间、倒数第二行时间和指标值。少量可读样本往往能很快发现触发行取错、初始化误判和窗口不足,比一开始只看整段收益曲线更可靠。
K 线使用清单
- 周期秒数、数据长度和合约代码与研究目标一致。
- 指标窗口已有足够的有效数据,没有把缺失值随意填成零。
- 监听
datetime判断新 K 线,监听价格字段判断当前线变化。 - 使用已完成 K 线时读取正确行,避免把正在形成的末行当成收盘结果。
- 研究连续合约与执行具体合约分开,回测结论不超出数据粒度。
把“动态序列、变化字段、已完成周期”这三个概念分清后,K 线代码就不再只是把 DataFrame 套进公式。每次计算为什么发生、使用的是哪一根线、结果能支持什么结论,都会有明确答案。
