前言
如果你刚开始接触国内期货量化,很可能先接触的是股票或外汇教程里的“K 线图”。期货里同样用 K 线把价格按时间切成一根根柱子:每根柱子有开盘价、最高价、最低价、收盘价和成交量。所谓1 分钟 K 线策略,指的是:你的交易规则主要依据每 1 分钟形成一根的这类 K 线来做判断(例如“上一根 1 分钟 K 线收盘突破 20 周期均线就做多”),而不是每一个成交 tick 都改一次主意。国内商品期货、金融期货都有日盘和夜盘,1 分钟线在交易时段内大约每分钟多一根,频率比 5 分钟、日线高很多,所以程序如果“每一口价都重新算一遍 1 分钟信号”,CPU 和报单次数都会失控。
用 Python 接行情时,天勤TqSdk的get_kline_serial会返回一张类似 Excel 的表(pandas DataFrame),每一行是一根 K 线,其中有一列叫datetime,表示这根 K 线对应的时间点(由行情服务端按交易所规则生成并推送,不是你自己在代码里sleep(60)计时出来的)。本文专门讲:在 1 分钟这种高频 K 线场景下,怎样用datetime判断“新的一根已经生成”,以及为什么算信号时常常用iloc[-2]而不是最后一行iloc[-1]。全文面向正在把“会画 K 线、会算均线”推进到“能自动下单”的期货量化初学者,不默认你已经熟悉 CTP 或天勤内部实现。
一、先弄清:1 分钟 K 线从天勤哪里来
订阅写法里,duration=60表示周期为 60 秒,即 1 分钟 K 线(单位是秒,不是“第 60 根”之类的别的含义):
fromtqsdkimportTqApi,TqAuth,TqSim api=TqApi(TqSim(),auth=TqAuth("快期账户","密码"))# symbol 形如 "交易所.合约",例如上期所螺纹钢某月份kl=api.get_kline_serial("SHFE.rb2510",60,data_length=300)kl表里常见列包括datetime、open、high、low、close、volume等。datetime是行情/K 线服务写入的业务时间字段:当交易所产生新的一分钟 bar 时,表里“当前正在形成的那一行”的datetime会变成新一分钟的标签,上一行的 OHLC 则不再变化。策略程序本身不生成datetime,只读取并比较它是否在本次wait_update之后相对上一帧发生了变化。
主循环仍须以api.wait_update()为驱动(等待天勤收到并合并行情包);没有wait_update,表里的数字不会随实盘推进。
二、标准写法:用 datetime 变化表示“新 bar 来了”
下面这段代码的含义可以逐行理解:
whileTrue:api.wait_update()ifnotapi.is_changing(kl.iloc[-1],"datetime"):continuesig=f(kl.close.iloc[-2],kl.volume.iloc[-2])kl.iloc[-1]:DataFrame 最后一行,对应当前这一分钟还在交易中的 K 线,收盘价等仍会随 tick 变动。api.is_changing(kl.iloc[-1], "datetime"):询问“这一次更新里,最后一行的datetime字段变了吗?”若为真,通常表示新的一根 1 分钟 K 线已经开始,上一根刚刚收盘定格。kl.iloc[-2]:倒数第二行,即上一根已走完的 1 分钟 K 线,适合作为“收盘突破”“均线交叉”类规则的计算对象。- 在交易时段内,满足条件的逻辑大约每分钟最多进入一次
f(休市时没有新 bar,datetime不推进,不会误触发)。
天勤官方文档对is_changing有说明:生成新 K 线时,最后一根各字段都可能算“有更新”;实务上盯datetime比盯整行更干净,避免 high/low 抖动导致重复进逻辑。
三、为什么不用 iloc[-1] 做“收盘信号”
很多初学者从 CSV 回测习惯“取最后一行就是最新数据”。在实时1 分钟 K 线里,最后一行是“未收盘”的:这一分钟内可能有几十上百个 tick,close会来回变。若你用iloc[-1]判断“收盘价上穿均线”,同一分钟内条件可能反复成立又失效,程序就会在 1 分钟内多次下单或反复改信号。
回测框架若按历史逐条推送 K 线,有时[-1]在某一帧恰好代表“已完成的 bar”,和实盘语义不一致,会出现回测很好看、实盘完全另一套的情况。团队若做期货量化,应书面规定:本策略的入场价、指标取值统一在iloc[-2](上一根已收盘 bar),回测、模拟、实盘同一套代码。
四、data_length 与指标冷启动
data_length=300表示向服务端申请约 300 根历史 1 分钟 K 线缓冲,用于计算例如 60 周期均线。若长度不够,ma60.iloc[-2]可能是nan,应对该分钟continue,不要拿nan比较大小。1 分钟策略往往指标周期数字不大,但 buffer 仍建议留足,避免刚启动前几十分钟乱信号。
五、夜盘、日盘与休市
国内期货很多品种有夜盘。休市期间没有新的 1 分钟 bar,datetime不会假装向前跳;策略若只在日盘交易,可结合get_quote返回的交易状态或自有交易日历,在过滤后再算信号,避免在代码里用sleep(60)“等下一根 K 线”——那会阻塞wait_update,反而收不到行情。
六、若还要结合盘口(tick)过滤
有的 1 分钟规则是:分钟收盘满足条件后,再看买卖价差或最新价才下单。正确顺序是:先datetime触发并算完分钟信号,再读get_quote的买卖盘;不要用每一个last_price变动去驱动“整段 1 分钟策略逻辑”,否则又回到 tick 级空转。
执行层若一分钟内可能多次调仓,天勤的TargetPosTask会在后续wait_update里撤单、改价;文档提醒勿与手写insert_order混用同一合约,并注意大单拆分的min_volume/max_volume参数(若启用)。
总结
1 分钟 K 线期货策略指的是按每 60 秒一根的 K 线做决策,而不是按每个 tick。天勤通过get_kline_serial(..., 60)得到带datetime的 K 线表;datetime来自行情服务而非本地计时。判断“新 bar”应使用api.is_changing(kl.iloc[-1], "datetime"),在触发后于iloc[-2]上计算信号,避免用未收盘的[-1]导致一分钟内重复交易或与回测不一致。
按上述写法,程序在交易时段大约每分钟处理一次策略逻辑,CPU 和报单节奏都可控。若你已从 CSV 研究迁移到天勤,请同时检查研究脚本是否用了“最后一行未收盘数据”,并改成与本文一致的[-2]规则。
FAQ
1)想做 15 秒、5 分钟 K 线怎么办?
把duration改成对应秒数(如 15、300),datetime与[-2]的规则不变,只是 bar 间隔不同。
2)datetime 和本机时间有什么区别?
应以 K 线表里的datetime(交易所/行情业务时间)为准,不要用time.sleep(60)代替 bar 推进。
3)感觉漏了一根 1 分钟 K 线?
排查断线重连后是否仍订阅同一合约、data_length是否过小、当日是否临停交易。
4)回测 1 分钟很慢?
回测同样应在循环里用is_changing(..., "datetime")过滤,避免每一历史 tick 都跑完整策略体。
风险提示
本文仅说明期货量化程序实现方式,不构成任何投资建议。实盘前请在模拟环境验证。