实时行情
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 类型,多个值用逗号分隔;入门阶段可留空 |
snapshot | True 表示一次性快照,False 表示短暂订阅后返回已有数据 |
timeout | 等待快照完成的秒数;快照请求通常至少给 11 秒 |
行情权限先看这里
Section titled “行情权限先看这里”行情请求不只看代码是否正确,还取决于账户是否有对应市场数据权限。
| 行情类型 | reqMarketDataType() | 说明 |
|---|---|---|
| 实时行情 | 1 | 需要账户订阅对应市场数据 |
| 冻结行情 | 2 | 市场关闭后可能返回最后冻结值 |
| 延迟行情 | 3 | 没有实时订阅时常用于学习和测试 |
| 延迟冻结行情 | 4 | 延迟行情的冻结版本 |
如果没有实时行情订阅,TWS 可能返回 10089 或 10167。这不一定代表代码错了,通常是权限或行情类型选择问题。
查询 AAPL 延迟行情快照
Section titled “查询 AAPL 延迟行情快照”下面示例先请求延迟行情类型,再读取 AAPL 快照。这样在没有美股实时行情订阅时,也更容易得到可用于学习的字段。
from decimal import Decimal
from ibapi.contract import Contractfrom 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=TrueFIELD_COUNT=12FIELDS=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_VOLUMEDELAYED_ASK=290.37DELAYED_BID=290.36DELAYED_LAST=291.13DELAYED_VOLUME=38012626347303IS_CONNECTED_AFTER_STOP=False同一环境下,如果不调用 reqMarketDataType(3),请求实时行情会返回:
ERROR 1 ... 10089 请求的市场数据对于API来说需要额外订阅。... 延迟市场数据可用。这说明账户没有对应实时行情订阅,但可以请求延迟行情。
常见返回字段
Section titled “常见返回字段”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 | 延迟最新成交时间戳 |
不同市场、权限和交易时段会影响字段是否出现。代码要把字段缺失当作正常情况处理,不要假设每次都有买价、卖价和最新价。
快照和订阅的区别
Section titled “快照和订阅的区别”| 模式 | 写法 | 适合场景 |
|---|---|---|
| 快照 | snapshot=True | 查询一次价格、教学示例、下单前粗略参考 |
| 短暂订阅 | snapshot=False | 等待一小段时间,拿到已有 tick 后取消 |
| 正式流式订阅 | 原始 reqMktData() + 自己维护回调 | 行情面板、策略持续运行 |
get_market_data_snapshot() 是同步封装,适合学习和简单脚本。正式交易系统如果要持续监听行情,应该使用异步回调模型,自己维护订阅、取消、缓存和断线重连。
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
10089 | 没有对应实时市场数据订阅 | 使用 reqMarketDataType(3) 请求延迟行情,或订阅实时行情 |
10167 | 没有实时权限,TWS 正在显示延迟行情 | 接受延迟字段,或购买实时行情权限 |
ResponseTimeout | 没有收到 tickSnapshotEnd() | 延长 timeout,检查合约是否正确和行情权限 |
| 返回字段很少 | 市场关闭、权限不足或该品种没有对应字段 | 代码里用 .get() 读取字段 |
字段名前面带 DELAYED_ | 返回的是延迟行情 | 不要把延迟价格当作实时交易信号 |