接收实时 5 秒线
请求建立成功后,TWS 会通过 realtimeBar() 推送每根 5 秒 bar。
def realtimeBar(self, reqId, time_, open_, high, low, close, volume, wap, count): """接收实时 5 秒 K 线。""" print( reqId, # 请求编号 time_, # bar 时间,Unix 秒级时间戳 open_, # 开盘价 high, # 最高价 low, # 最低价 close, # 收盘价 volume, # 成交量 wap, # 加权平均价 count, # 成交笔数 )回调的每一行就是一根 5 秒 bar。不要在 reqRealTimeBars() 调用处等待返回值;TWS API 的数据是异步回调模型。
| 字段 | 中文解释 | 使用建议 |
|---|---|---|
reqId | 请求编号,用来区分是哪一个订阅。 | 和订阅表、合约信息一起关联,不建议单独作为数据库主键。 |
time_ | bar 对应时间,Unix 秒级时间戳。 | 保存时建议转成 UTC,同时保留原始秒数方便排查。 |
open_ | 这 5 秒内的开盘价。 | Python 里用 open_ 是为了避开内置函数 open。 |
high | 这 5 秒内的最高价。 | 用于图表和高低价判断。 |
low | 这 5 秒内的最低价。 | 用于图表和高低价判断。 |
close | 这 5 秒内的收盘价。 | 常作为策略最新价格输入。 |
volume | 成交量。 | 只有 TRADES 更接近成交量语义;其他类型不要直接当成交量。 |
wap | 加权平均价。 | 可用于成交均价观察,字段名含义是“按成交量加权后的价格”。 |
count | 成交笔数。 | 对 TRADES 更有意义;报价类数据可能没有成交笔数语义。 |
如果 whatToShow 不是 TRADES,部分字段的含义会随数据类型变化。例如 MIDPOINT 主要关注价格变化,成交量、成交笔数可能没有实际意义。保存数据时应同时保存 whatToShow,否则后面很难判断一根 bar 是成交聚合还是报价聚合。
| 字段 | 建议 |
|---|---|
reqId | 用于关联订阅、回调和错误,不建议作为数据库主键 |
symbol / conId | 和 bar 一起保存,方便多合约订阅 |
time_ | 转为 UTC 时间后保存 |
raw_time | 可选保存原始 Unix 秒 |
| OHLCV | 按数值字段保存,不要保存成格式化字符串 |
一张实用的数据表通常至少包含:
conId, symbol, exchange, whatToShow, useRTH, barTimeUtc,open, high, low, close, volume, wap, count, sourceReqId这样后面排查时能看出这根 bar 是哪个合约、哪种数据类型、是否只包含常规交易时段。
权限错误时没有回调
Section titled “权限错误时没有回调”如果请求被行情权限拒绝,realtimeBar() 不会收到数据。AAPL 参考输出如下:
BAR_ROWS=0ERROR=reqId=97201;code=420;msg=实时查询无效:No market data permissions for ISLAND STK. 请求的市场数据对于API来说需要额外订阅。点击“市场数据连接”对话框中的链接获取更多详情。因此代码里必须同时实现 error(),不能只等待 realtimeBar()。
最小接收结构
Section titled “最小接收结构”bars_by_req_id = {}
def realtimeBar(self, reqId, time_, open_, high, low, close, volume, wap, count): bar = { "time": time_, "open": open_, "high": high, "low": low, "close": close, "volume": volume, "wap": wap, "count": count, } bars_by_req_id.setdefault(reqId, []) bars_by_req_id[reqId] += [bar]真实项目里还应保存合约信息、whatToShow 和 useRTH,否则多个订阅并行时很难判断数据来源。