跳转到内容

API Socket 连接断开

TWS API 程序不能假设连接永远在线。TWS 重启、每日维护、每周重新认证、网络变化、电脑休眠、端口修改、clientId 冲突和限频违规,都可能让 API Socket 断开。

断线处理不是只调用一次 connect()。更完整的流程是:

识别断线 -> 停止发送新请求 -> 重新连接 -> 等 nextValidId -> 恢复订阅和状态

如果只重连 socket,却没有恢复行情订阅、账户订阅、订单状态和程序里的 reqId 映射,程序表面在线,实际业务状态仍然可能是空的或过期的。

信号含义
connectionClosed()Python API 检测到 socket 关闭时触发的回调
isConnected() 返回 False这个客户端对象不再认为自己在线
error() 收到连接相关 codeTWS API 把很多连接通知也放在 error() 回调里
请求时报 504 Not connected程序已经不在线,却继续发送请求
新连接时报 502客户端无法连接到 TWS / IB Gateway 端口

Python API 源码里,底层连接关闭会触发 wrapper.connectionClosed()EClient.isConnected() 会检查 socket 状态。因此程序里应该同时处理回调和主动状态检查。

Code常见含义处理方式
1100TWS 与 IBKR 后端连接丢失暂停请求,等待恢复或准备重连
1101TWS 与 IBKR 后端连接恢复,但数据可能丢失重新订阅行情、账户、扫描器等流式请求
1102TWS 与 IBKR 后端连接恢复,数据仍被保留仍建议检查关键订阅状态
1300TWS Socket 端口被重置读取新端口后重新连接
502API 客户端无法连接到 TWS检查 TWS 是否运行、API 是否启用、端口和防火墙
504API 客户端未连接停止发送业务请求,进入重连流程
326clientId 已被使用换独立 clientId 或关闭冲突客户端
100消息速率超过限制降低请求速率,避免继续触发断开或拒绝

110011011102 更偏 TWS 到 IBKR 后端的连接状态;connectionClosed()502504 更偏你的程序到 TWS 的本地 socket 状态。两类问题需要分开看。

下面示例展示结构:记录断线信号、等待 nextValidId()、并把恢复订阅留成单独函数。实际项目中,restore_subscriptions() 里应该重新请求行情、账户、持仓、扫描器或订单状态。

import threading
import time
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
class App(EWrapper, EClient):
def __init__(self):
EClient.__init__(self, self)
self.ready = threading.Event()
self.closed = threading.Event()
self.next_order_id = None
def nextValidId(self, orderId: int):
# 重连后也会收到新的 nextValidId。
self.next_order_id = orderId
self.ready.set()
def connectionClosed(self):
# socket 关闭后,停止发送新请求,并交给外层重连循环处理。
print("API socket 已关闭")
self.ready.clear()
self.closed.set()
def error(self, reqId, errorCode, errorString, advancedOrderRejectJson=""):
print(f"API message: reqId={reqId}, code={errorCode}, message={errorString}")
if errorCode in (1100, 1101, 1102, 1300, 502, 504, 326, 100):
# 这里不要直接重连;先记录状态,让主循环统一处理。
self.closed.set()
def restore_subscriptions(self):
# 按你的业务重新订阅,例如:
# self.reqAccountSummary(...)
# self.reqPositions()
# self.reqMktData(...)
pass
def connect_once(host: str, port: int, client_id: int) -> App:
app = App()
app.connect(host, port, clientId=client_id)
thread = threading.Thread(target=app.run, daemon=True)
thread.start()
if not app.ready.wait(10):
app.disconnect()
raise TimeoutError("连接超时:没有收到 nextValidId")
app.restore_subscriptions()
return app
HOST = "127.0.0.1"
PORT = 7497
CLIENT_ID = 401
while True:
try:
app = connect_once(HOST, PORT, CLIENT_ID)
while app.isConnected() and not app.closed.wait(1):
# 主循环可以在这里做心跳、队列消费或业务调度。
pass
app.disconnect()
except Exception as exc:
print(f"连接不可用,准备稍后重试: {exc}")
time.sleep(5)

这只是连接层骨架,不代表完整交易系统。真实系统还要处理请求去重、订单恢复、持仓同步、限频、日志和异常告警。

断线恢复后,至少检查这些状态:

状态为什么要恢复
行情订阅流式行情断开后通常要重新请求
账户和持仓程序内缓存可能已经过期
订单状态断线期间订单可能成交、取消或被系统拒绝
nextValidId下单前必须使用有效的订单 ID
reqId 映射旧请求和新请求不要混在一起
限频计数重连后不要一次性补发过多请求

最容易漏的是订单状态。程序断线期间,TWS 和 IBKR 后端仍可能继续处理已经提交的订单。重连后要主动请求开放订单、完成订单或执行明细,不能只相信断线前的内存状态。

场景建议处理
TWS 每日自动重启程序进入等待状态,TWS 恢复后重新连接
每周重新认证等人工完成认证后再重连
Wi-Fi 或服务器网络抖动使用退避重试,不要高频重连
clientId 冲突停止冲突程序或换编号
端口被改成新值更新配置,按新端口连接
限频导致连接不稳定降低请求速率,先恢复连接再恢复业务

不要让多个线程同时重连同一个 App 对象。更稳的方式是:断开旧对象,创建新对象,等待 nextValidId(),再恢复业务状态。

  1. 先确认 TWS / IB Gateway 还在运行并已登录。
  2. 用最小 reqCurrentTime() 脚本验证端口是否还通。
  3. error() 输出是否有 1100110111021300502504
  4. 确认没有另一个程序占用相同 clientId
  5. 重连成功后,先恢复账户和订单状态,再恢复批量行情。

如果最小连接脚本都失败,不要先改业务代码。连接层没恢复之前,合约、行情和订单接口都会跟着失败。

IBKR Campus: TWS API Documentation