跳转到内容

取消实时 5 秒线

实时 5 秒线是持续订阅。只要不取消,它就可能一直占用行情行数和程序资源。页面关闭、策略停止、合约切换时,都应该主动调用取消接口。

app.cancelRealTimeBars(97201) # 97201 必须和 reqRealTimeBars() 的 reqId 一致

cancelRealTimeBars() 没有单独的成功回调。通常做法是在程序自己的订阅状态表里把 reqId 标记为已取消,并继续短时间接收可能已经在路上的回调。

场景是否需要取消
用户关闭图表需要
策略停止运行需要
合约从 AAPL 切到 MSFT需要取消旧合约再订阅新合约
请求一开始就权限失败可以调用取消,但可能收到 300

请求 AAPL 5 秒线后主动取消:

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

这里的 300 不是新的连接问题,而是因为权限错误导致订阅没有真正建立,取消时 TWS 找不到这个 reqId 对应的有效订阅。

建议原因
保存所有活跃 reqId方便页面退出或程序停止时批量取消
权限错误后标记订阅失败避免重复取消同一个失败请求
取消后等待短时间确认无新回调防止残留数据写入已关闭的图表
日志里记录取消动作排查行情行数泄漏时很有用
active_realtime_bars = set()
def subscribe_realtime_bars(req_id, contract):
app.reqRealTimeBars(req_id, contract, 5, "TRADES", True, [])
active_realtime_bars.add(req_id)
def cancel_realtime_bars(req_id):
if req_id not in active_realtime_bars:
return
app.cancelRealTimeBars(req_id)
active_realtime_bars.remove(req_id)
def error(self, reqId, code, message):
if code in {200, 420, 10089}:
active_realtime_bars.discard(reqId)

这个示例的重点是:权限错误或合约错误出现后,程序要把订阅从活跃集合移除。这样页面关闭时不会对同一个失败请求反复取消,也更容易判断 300 是否只是跟随错误。