跳转到内容

实时行情

get_market_data_snapshot() 用来读取某个合约的行情快照。它对应原始 TWS API 的:

reqMktData(reqId, contract, genericTickList, snapshot, regulatorySnapshot, mktDataOptions)
-> tickPrice(reqId, tickType, price, attrib)
-> tickSize(reqId, tickType, size)
-> tickString(reqId, tickType, value)
-> tickSnapshotEnd(reqId)

同步封装会收集 tickPrice()tickSize()tickString() 等回调,在收到 tickSnapshotEnd() 后返回字典。

本地官方源码中的定义核心如下:

def get_market_data_snapshot(self, contract, generic_tick_list="", snapshot=True, timeout=None):
if timeout is None:
timeout = 11 if snapshot == True else 5
req_id = self.get_next_valid_id()
self.reqMktData(req_id, contract, generic_tick_list, snapshot, False, [])
if snapshot:
data = self._wait_for_response(req_id, "market_data", timeout)
else:
time.sleep(1 if timeout is None or timeout > 1 else timeout / 2)
data = self.market_data.get(req_id, {})
self.cancelMktData(req_id)
return data

参数说明:

参数含义
contract要读取行情的 Contract 对象
generic_tick_list额外 tick 类型,多个值用逗号分隔;入门阶段可留空
snapshotTrue 表示一次性快照,False 表示短暂订阅后返回已有数据
timeout等待快照完成的秒数;快照请求通常至少给 11 秒

行情请求不只看代码是否正确,还取决于账户是否有对应市场数据权限。

行情类型reqMarketDataType()说明
实时行情1需要账户订阅对应市场数据
冻结行情2市场关闭后可能返回最后冻结值
延迟行情3没有实时订阅时常用于学习和测试
延迟冻结行情4延迟行情的冻结版本

如果没有实时行情订阅,TWS 可能返回 1008910167。这不一定代表代码错了,通常是权限或行情类型选择问题。

下面示例先请求延迟行情类型,再读取 AAPL 快照。这样在没有美股实时行情订阅时,也更容易得到可用于学习的字段。

from decimal import Decimal
from ibapi.contract import Contract
from ibapi.sync_wrapper import TWSSyncWrapper, ResponseTimeout
def stock_contract(symbol: str, currency: str = "USD") -> Contract:
contract = Contract()
contract.symbol = symbol
contract.secType = "STK"
contract.exchange = "SMART"
contract.currency = currency
return contract
app = TWSSyncWrapper(timeout=15)
try:
if not app.connect_and_start("127.0.0.1", 7497, 948):
raise RuntimeError("连接 TWS API 失败")
# 3 表示延迟行情。已有实时权限时,也可以改成 1。
app.reqMarketDataType(3)
data = app.get_market_data_snapshot(
stock_contract("AAPL"),
snapshot=True,
timeout=15,
)
print(f"FIELD_COUNT={len(data)}")
print("FIELDS=" + ",".join(sorted(data.keys())))
for key in sorted(data.keys()):
value = data[key]
if isinstance(value, Decimal):
value = str(value)
print(f"{key}={value}")
except ResponseTimeout:
print("行情快照超时:可能没有行情权限,或没有收到 tickSnapshotEnd 回调")
finally:
app.disconnect_and_stop()

使用 TWS 模拟账户、127.0.0.1:7497、独立 clientId 检查行情快照时,行情价格会随市场变化,下面只用于说明返回结构:

CONNECTED=True
FIELD_COUNT=12
FIELDS=DELAYED_ASK,DELAYED_ASK_SIZE,DELAYED_BID,DELAYED_BID_SIZE,DELAYED_CLOSE,DELAYED_HIGH,DELAYED_LAST,DELAYED_LAST_SIZE,DELAYED_LAST_TIMESTAMP,DELAYED_LOW,DELAYED_OPEN,DELAYED_VOLUME
DELAYED_ASK=290.37
DELAYED_BID=290.36
DELAYED_LAST=291.13
DELAYED_VOLUME=38012626347303
IS_CONNECTED_AFTER_STOP=False

同一环境下,如果不调用 reqMarketDataType(3),请求实时行情会返回:

ERROR 1 ... 10089 请求的市场数据对于API来说需要额外订阅。... 延迟市场数据可用。

这说明账户没有对应实时行情订阅,但可以请求延迟行情。

TWSSyncWrapper 会把 tick 类型转换成字符串键。常见字段如下:

字段中文含义
BID / DELAYED_BID买一价
BID_SIZE / DELAYED_BID_SIZE买一数量
ASK / DELAYED_ASK卖一价
ASK_SIZE / DELAYED_ASK_SIZE卖一数量
LAST / DELAYED_LAST最新成交价
LAST_SIZE / DELAYED_LAST_SIZE最新成交数量
HIGH / DELAYED_HIGH当日最高
LOW / DELAYED_LOW当日最低
OPEN / DELAYED_OPEN当日开盘
CLOSE / DELAYED_CLOSE前收或收盘相关字段
VOLUME / DELAYED_VOLUME成交量
DELAYED_LAST_TIMESTAMP延迟最新成交时间戳

不同市场、权限和交易时段会影响字段是否出现。代码要把字段缺失当作正常情况处理,不要假设每次都有买价、卖价和最新价。

模式写法适合场景
快照snapshot=True查询一次价格、教学示例、下单前粗略参考
短暂订阅snapshot=False等待一小段时间,拿到已有 tick 后取消
正式流式订阅原始 reqMktData() + 自己维护回调行情面板、策略持续运行

get_market_data_snapshot() 是同步封装,适合学习和简单脚本。正式交易系统如果要持续监听行情,应该使用异步回调模型,自己维护订阅、取消、缓存和断线重连。

现象常见原因处理方式
10089没有对应实时市场数据订阅使用 reqMarketDataType(3) 请求延迟行情,或订阅实时行情
10167没有实时权限,TWS 正在显示延迟行情接受延迟字段,或购买实时行情权限
ResponseTimeout没有收到 tickSnapshotEnd()延长 timeout,检查合约是否正确和行情权限
返回字段很少市场关闭、权限不足或该品种没有对应字段代码里用 .get() 读取字段
字段名前面带 DELAYED_返回的是延迟行情不要把延迟价格当作实时交易信号

IBKR Campus: TWS API Documentation