跳转到内容

接收实时 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 是哪个合约、哪种数据类型、是否只包含常规交易时段。

如果请求被行情权限拒绝,realtimeBar() 不会收到数据。AAPL 参考输出如下:

BAR_ROWS=0
ERROR=reqId=97201;code=420;msg=实时查询无效:No market data permissions for ISLAND STK. 请求的市场数据对于API来说需要额外订阅。点击“市场数据连接”对话框中的链接获取更多详情。

因此代码里必须同时实现 error(),不能只等待 realtimeBar()

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]

真实项目里还应保存合约信息、whatToShowuseRTH,否则多个订阅并行时很难判断数据来源。