手把手详细学习内容:合约查询、Quote 字段与行情对象读法
今天继续根据 TqSdk 文档做具体学习:不急着写下单逻辑,而是先学会"找合约、看合约身份、读 Quote 字段、判断字段是否有效"。只有你能稳定识别合约和字段,后面的交易、回测、风控脚本才不会建立在错误行情上。
今天的学习方式
不是计划表,而是边读边做。每个脚本都会告诉你:现在做什么、为什么做、应该看到什么、出错怎么查。
今天仍然以行情学习为主,通常不需要真实交易账户;示例只需要填写 TqAuth 账号密码。
示例优先使用主力合约符号,减少固定交割月份过期导致的学习干扰。
0. 先接上第 4 天:今天要把"行情数据"读得更准确
第 4 天你已经会订阅 K 线和 Tick,并知道它们会随着 wait_update 原地更新。今天继续向前一步:当你拿到一个 quote、K 线或 Tick 时,你要知道哪些字段该读、字段代表什么、字段什么时候可能还没有有效值。
**今天你要形成的核心感觉:**TqSdk 里的行情对象不是一堆随便打印的属性,而是有用途分层的:先看合约身份,再看交易状态,再看价格盘口,最后才做价差、最小变动价位、合约乘数等计算。
今天不要犯的第一个错
不要在策略里只凭 symbol 字符串猜合约身份。
正确做法是:用 quote 或 query_symbol_info 看 instrument_name、instrument_id、exchange_id、ins_class、expired 等字段。
1. 先认识 Quote:它是实时行情对象,不是一次性快照
TqSdk 文档中,get_quote 用来获取单个合约的实时行情。它返回的 quote 对象会在 wait_update 之后被更新。因此你的代码结构仍然是:循环外 get_quote 一次,循环里 wait_update,然后读取字段变化。
2. 现在动手:打印 Quote 的最小可用字段集请新建 day5_quote_min_fields.py。这个脚本只做一件事:订阅螺纹钢主力合约 quote,等行情更新后打印最小字段集。
day5_quote_min_fields.py
from tqsdk import TqApi, TqAuth
api = TqApi(auth=TqAuth("快期账户", "账户密码"))
quote = api.get_quote("KQ.m@SHFE.rb")
try:
while True:
api.wait_update()
# 只在你关心的价格和盘口字段变化时打印
if api.is_changing(quote, ["last_price", "ask_price1", "bid_price1"]):
print("时间:", quote.datetime)
print("合约名:", quote.instrument_name)
print("最新价:", quote.last_price)
print("买一/卖一:", quote.bid_price1, quote.ask_price1)
print("成交量:", quote.volume)
print("持仓量:", quote.open_interest)
break
finally:
api.close()
2.1 运行脚本
命令行运行
python day5_quote_min_fields.py
你应该看到时间、合约名、最新价、买一、卖一、成交量和持仓量。今天先不要把 quote 全部打印出来;先养成"只打印当前任务需要的字段"的习惯。
如果没有很快看到输出
先确认是否为交易时段,或者该品种当前是否有行情变化。
确认 TqAuth 的账号密码是否正确。
确认 symbol 没写错;主力合约 KQ.m@SHFE.rb 通常比固定月份更适合作为学习样例。
如果行情字段一开始为空或不是有效数字,先等待 wait_update 收到业务数据。
3. 逐行拆解:为什么只监听三个字段
**老师提醒:**is_changing 不是必须永远只监听三个字段。今天这样写,是为了训练你"按任务过滤变化"的习惯;后面写策略时,要根据触发条件选择字段。
4. 现在动手:看清合约身份,不要只看价格
交易脚本出错,有时不是算法错,而是合约认错。下面脚本专门打印合约身份字段,帮助你确认:这个 symbol 最终对应什么合约、是否过期、交易所和品种是什么。
day5_contract_identity.py
from tqsdk import TqApi, TqAuth
api = TqApi(auth=TqAuth("快期账户", "账户密码"))
quote = api.get_quote("KQ.m@SHFE.rb")
try:
while True:
api.wait_update()
if api.is_changing(quote):
print("instrument_name:", quote.instrument_name)
print("instrument_id:", quote.instrument_id)
print("exchange_id:", quote.exchange_id)
print("ins_class:", quote.ins_class)
print("underlying_symbol:", quote.underlying_symbol)
print("expired:", quote.expired)
break
finally:
api.close()
今天的关键习惯
看到一个 symbol,不要只问“价格是多少”。
还要问:它是不是我要的品种?是不是当前可用?有没有过期?交易所和合约类型是否符合预期?
5. 现在动手:用 query_cont_quotes 找主力合约
固定交割月份会过期,学习文档和长期样例中更适合使用主力合约或先查询当前可用合约。下面脚本用 query_cont_quotes 查询上海期货交易所螺纹钢相关连续/主力合约。
day5_query_cont_quotes.py
from tqsdk import TqApi, TqAuth
with TqApi(auth=TqAuth("快期账户", "账户密码")) as api:
conts = api.query_cont_quotes(exchange_id="SHFE", product_id="rb")
print("查询到的连续/主力合约:")
for symbol in conts:
print(symbol)
你应该看到一组连续或主力相关合约代码。实际返回内容会随交易所、品种和当前合约状态变化。这里的学习重点是:不要死记一个固定月份,而是学会查询。
**实战迁移:**写教学代码、监控脚本或长期运行工具时,优先考虑主力/连续合约或先查询当前合约;写真实交易时,则必须非常清楚自己下单的是具体哪个合约。
6. 现在动手:用 query_symbol_info 查看静态信息表
query_symbol_info 返回的是静态合约信息表,不是 quote 这样的实时对象。它适合一次性查看合约名称、类型、最小变动价位、合约乘数等基础信息。
day5_symbol_info.py
from tqsdk import TqApi, TqAuth
with TqApi(auth=TqAuth("快期账户", "账户密码")) as api:
symbols = api.query_cont_quotes(exchange_id="SHFE", product_id="rb")
info = api.query_symbol_info(list(symbols))
cols = ["instrument_id", "instrument_name", "ins_class", "price_tick", "volume_multiple"]
print(info[cols])
7. 认识 price_tick:价格不能随便写小数
TqSdk 文档中的 quote.price_tick 表示最小价格变动单位。以后你下限价单时,价格必须符合交易所允许的最小变动价位。今天先不下单,只做价格检查。
day5_price_tick_and_spread.py
from math import isfinite
from tqsdk import TqApi, TqAuth
api = TqApi(auth=TqAuth("快期账户", "账户密码"))
quote = api.get_quote("KQ.m@SHFE.rb")
try:
while True:
api.wait_update()
if api.is_changing(quote, ["ask_price1", "bid_price1", "price_tick"]):
print("最小变动价位:", quote.price_tick)
print("买一:", quote.bid_price1)
print("卖一:", quote.ask_price1)
if isfinite(quote.ask_price1) and isfinite(quote.bid_price1):
spread = quote.ask_price1 - quote.bid_price1
print("买卖价差:", spread)
break
finally:
api.close()
为什么这里用 isfinite
行情刚订阅时,某些价格字段可能还没有有效数值。
计算价差前先判断是否为有效数字,可以避免把无效字段拿去做运算。
这个习惯后面写下单价格、止损价格、滑点逻辑时非常重要。
quote.volume_multiple 表示合约乘数。期货价格变化 1 个点对应的盈亏,通常要乘以合约乘数;如果价格只变化一个 price_tick,也要用 price_tick * volume_multiple 估算每手跳动价值。
day5_tick_value.py
from math import isfinite
from tqsdk import TqApi, TqAuth
api = TqApi(auth=TqAuth("快期账户", "账户密码"))
quote = api.get_quote("KQ.m@SHFE.rb")
try:
while True:
api.wait_update()
if api.is_changing(quote):
print("合约:", quote.instrument_name)
print("最小变动价位:", quote.price_tick)
print("合约乘数:", quote.volume_multiple)
if isfinite(quote.price_tick) and isfinite(quote.volume_multiple):
tick_value = quote.price_tick * quote.volume_multiple
print("每手每跳价值估算:", tick_value)
break
finally:
api.close()
**老师提醒:**今天只是行情字段学习,不做保证金和盈亏完整试算。后面学习账户、持仓和 TqScenario 时,才会把手续费、保证金率、持仓方向等因素一起考虑。
9. 现在动手:查看交易状态
get_trading_status 用来查看合约当前交易状态。你在非交易时段运行行情脚本时,行情变化少并不一定代表代码错了,先看交易状态会更稳。
day5_trading_status.py
from tqsdk import TqApi, TqAuth
with TqApi(auth=TqAuth("快期账户", "账户密码")) as api:
status = api.get_trading_status("KQ.m@SHFE.rb")
print("交易状态:", status)
如何理解这个脚本
它不是交易信号,只是帮助你判断合约当前交易状态。
如果非交易时段 quote 更新很少,先不要急着改代码。
调试行情脚本时,合约状态、交易时间、品种活跃度都要一起看。
10. 错误实验一:用固定过期合约做长期样例
下面是常见错误:在学习文档或长期脚本中写死某个交割月份。这个代码今天可能能跑,过一段时间可能就因为合约过期而不适合继续使用。
不推荐作为长期学习样例
# 容易失效的学习写法:固定月份可能会过期
quote = api.get_quote("SHFE.rb2505")
改法是:学习行情订阅时优先用主力合约;需要具体月份时,先通过 query_quotes 或 query_symbol_info 确认合约仍然有效,再继续写后续逻辑。
更适合学习阶段
# 更稳的学习写法:使用主力合约符号,或者先查询当前合约
quote = api.get_quote("KQ.m@SHFE.rb")
11. 错误实验二:把 query_symbol_info 当实时行情用
query_symbol_info 返回的是静态信息表,适合看合约基础资料;如果你想监听最新价、买一卖一、成交量变化,应该用 get_quote。
错误思路
# 错误思路:拿静态信息表去等待实时价格变化
info = api.query_symbol_info(["KQ.m@SHFE.rb"])
while True:
api.wait_update()
print(info) # 这不是实时 quote 监听方式
正确思路
# 正确思路:实时行情用 get_quote
quote = api.get_quote("KQ.m@SHFE.rb")
while True:
api.wait_update()
if api.is_changing(quote, ["last_price", "ask_price1", "bid_price1"]):
print(quote.datetime, quote.last_price, quote.bid_price1, quote.ask_price1)
12. 把今天的字段读法整理成一张速查表
13. 常见错误排查:第 5 天专用清单
☐ 我是否把 quote 当成一次性快照?如果是,改成 wait_update 循环中读取。
☐ 我是否只看 last_price,不看合约是否过期、交易所和合约类型?
☐ 我是否用固定交割月份写长期学习样例?如果是,考虑主力合约或先查询合约。
☐ 我是否把 query_symbol_info 当成实时行情监听对象?实时价格变化应使用 get_quote。
☐ 我计算 bid/ask 价差前,是否确认字段是有效数字?
☐ 我是否把 price_tick 和 volume_multiple 忘掉了?以后下单价格和盈亏估算都会用到。
☐ 我在非交易时段测试时,是否先看了 trading_status?
☐ 我是否在 while True 里面反复 get_quote?正确做法通常是循环外订阅一次。
14. 课堂练习:你现在来写,写完再看答案
练习 1:打印 quote 的身份字段
要求:订阅 KQ.m@SHFE.rb,打印 instrument_name、instrument_id、exchange_id、ins_class、expired。
参考答案
from tqsdk import TqApi, TqAuth
api = TqApi(auth=TqAuth("快期账户", "账户密码"))
quote = api.get_quote("KQ.m@SHFE.rb")
try:
while True:
api.wait_update()
if api.is_changing(quote):
print(quote.instrument_name, quote.instrument_id, quote.exchange_id, quote.ins_class, quote.expired)
break
finally:
api.close()
练习 2:打印买一卖一价差
要求:当 bid_price1 或 ask_price1 变化时,打印价差。计算前要判断两个字段是否有效。
参考答案
from math import isfinite
from tqsdk import TqApi, TqAuth
api = TqApi(auth=TqAuth("快期账户", "账户密码"))
quote = api.get_quote("KQ.m@SHFE.rb")
try:
while True:
api.wait_update()
if api.is_changing(quote, ["bid_price1", "ask_price1"]):
if isfinite(quote.bid_price1) and isfinite(quote.ask_price1):
print("价差:", quote.ask_price1 - quote.bid_price1)
break
finally:
api.close()
练习 3:说出下面代码的问题
题目代码
info = api.query_symbol_info(["KQ.m@SHFE.rb"])
while True:
api.wait_update()
print(info["last_price"])
答案:query_symbol_info 是静态信息表,不是实时 quote;它不适合监听 last_price。实时价格应使用 quote = api.get_quote(…),然后在 wait_update 循环中读取 quote.last_price。
练习 4:什么时候先查询合约?
题目:你准备写一个长期使用的螺纹钢行情监控脚本,应该直接写死 SHFE.rb2505,还是优先考虑 KQ.m@SHFE.rb 或 query_cont_quotes?
答案:长期学习或监控样例优先考虑主力合约或先查询当前可用合约。固定月份合约会过期,适合在你明确要交易某个具体合约时使用。
15. 今日复盘:你要能独立说出来的 10 句话
· get_quote 返回的是会随 wait_update 更新的实时行情对象。
· quote.datetime 可以帮助确认行情时间。
· quote.last_price 是最新成交价,但不能只看它。
· bid_price1 和 ask_price1 是买一和卖一,计算价差前要确认有效。
· price_tick 是最小变动价位,后续下单价格必须考虑它。
· volume_multiple 是合约乘数,后续估算每跳价值和盈亏会用到。
· instrument_name、instrument_id、exchange_id、ins_class 用来确认合约身份。
· expired 用来判断合约是否过期。
· query_symbol_info 是静态信息表,不是实时行情对象。
· 长期学习样例不要随便写死固定交割月份,优先考虑主力合约或先查询。
16. 今天的完整最终版脚本
最后把今天最重要的写法合并成一个脚本:先查询主力合约,再订阅 quote,打印合约身份、交易状态、盘口价差和每手每跳价值。
day5_final_quote_contract_fields.py
from math import isfinite
from tqsdk import TqApi, TqAuth
api = TqApi(auth=TqAuth("快期账户", "账户密码"))
try:
symbols = api.query_cont_quotes(exchange_id="SHFE", product_id="rb")
print("查询到的连续/主力合约:", list(symbols))
symbol = "KQ.m@SHFE.rb"
quote = api.get_quote(symbol)
status = api.get_trading_status(symbol)
print("交易状态:", status)
while True:
api.wait_update()
if api.is_changing(quote, ["last_price", "bid_price1", "ask_price1"]):
print("合约名称:", quote.instrument_name)
print("合约代码:", quote.instrument_id)
print("交易所:", quote.exchange_id)
print("合约类型:", quote.ins_class)
print("是否过期:", quote.expired)
print("行情时间:", quote.datetime)
print("最新价:", quote.last_price)
print("买一/卖一:", quote.bid_price1, quote.ask_price1)
print("最小变动价位:", quote.price_tick)
print("合约乘数:", quote.volume_multiple)
if isfinite(quote.bid_price1) and isfinite(quote.ask_price1):
print("买卖价差:", quote.ask_price1 - quote.bid_price1)
if isfinite(quote.price_tick) and isfinite(quote.volume_multiple):
print("每手每跳价值估算:", quote.price_tick * quote.volume_multiple)
break
finally:
api.close()
17. 明天开始前的准备
明天会继续向交易账户和账户类型过渡。开始前,你应该确保今天的 final 脚本能运行,并且你能解释:quote 为什么要在 wait_update 后读,query_symbol_info 为什么不是实时行情,price_tick 和 volume_multiple 为什么重要。
第 5 天完成标准
你能独立写出 get_quote 的基本脚本。
你能说出 quote 常用字段的含义。
你能用 query_cont_quotes 或 query_symbol_info 辅助确认合约。
你知道固定月份合约会过期,长期样例要谨慎。
你能在计算价差或跳动价值前判断字段是否有效。





