建立 API 连接
建立 API 连接,是所有 TWS API 程序的第一步。你的 Python、Java、C# 或 C++ 程序并不是直接连交易所,而是先连接已经登录好的 TWS / IB Gateway。
可以把链路理解成这样:
你的程序 -> Socket 端口 -> TWS / IB Gateway -> IBKR 后端程序端是客户端,TWS / IB Gateway 是本地服务端。程序先打开 TCP socket,完成 API 版本握手,然后 TWS / IB Gateway 才会把账户、下一个可用订单 ID、连接时间等会话信息回调给程序。
连接前必须确认
Section titled “连接前必须确认”连接代码写对之前,先确认 TWS / IB Gateway 本身已经准备好:
| 检查项 | 常见值 | 说明 |
|---|---|---|
| 已登录账户 | 模拟账户或真实账户 | 新手建议先用模拟账户 |
| 启用 Socket API | Enable ActiveX and Socket Clients | TWS API 设置中必须打开 |
| 回环连接地址 | 127.0.0.1 | 程序和 TWS 在同一台电脑上运行时使用 |
| TWS 模拟账户端口 | 7497 | 常见 Paper Trading 端口 |
| TWS 真实账户端口 | 7496 | 真实账户下单前必须再次确认 |
| IB Gateway 模拟账户端口 | 4002 | 服务器部署常见选择 |
| IB Gateway 真实账户端口 | 4001 | 真实账户谨慎使用 |
端口不是写死规则,最终以你 TWS / IB Gateway 的 API Settings 里显示的 Socket Port 为准。
三个连接参数
Section titled “三个连接参数”Python 里通常这样连接:
app.connect("127.0.0.1", 7497, clientId=1)这三个参数分别是:
| 参数 | 含义 | 新手建议 |
|---|---|---|
host | TWS / IB Gateway 所在机器地址 | 同一台电脑运行时先用 127.0.0.1 |
port | TWS / IB Gateway 的 API Socket 端口 | 模拟账户先确认是否为 7497 |
clientId | API 客户端编号 | 不同脚本使用不同 clientId |
clientId 用来区分多个 API 程序。TWS 同一会话可以接受多个 API 客户端,但不要让两个正在运行的程序使用同一个 clientId,否则可能出现连接冲突、订单 ID 混乱或 socket 异常。
不要在 connect 后立刻发业务请求
Section titled “不要在 connect 后立刻发业务请求”connect() 成功只代表 socket 已经打开,不代表所有 API 会话信息都准备好了。
更稳的判断方式是等待 nextValidId() 回调。这个回调表示 TWS / IB Gateway 已经完成初始握手,并给出了下一个可用订单 ID。官方也建议通常把 nextValidId 作为连接完成、可以开始发送其他请求的信号。
错误示例:
app.connect("127.0.0.1", 7497, clientId=1)app.reqCurrentTime() # 太早,连接握手可能还没完成更稳的方式:
def nextValidId(self, orderId: int): self.reqCurrentTime()Python 最小连接示例
Section titled “Python 最小连接示例”下面示例只做一件事:连接 TWS,等到 nextValidId() 后请求服务器时间,收到 currentTime() 后断开。
import threadingimport time
from ibapi.client import EClientfrom ibapi.wrapper import EWrapper
class App(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.done = threading.Event() self.server_time = None
def nextValidId(self, orderId: int): # 收到 nextValidId 后,说明初始握手已经完成。 self.ready.set()
# 这里请求 TWS / IB Gateway 返回服务器时间。 self.reqCurrentTime()
def currentTime(self, time_: int): # time_ 是 Unix 时间戳,表示 IBKR 服务器时间。 self.server_time = time_ self.done.set() self.disconnect()
def error(self, reqId, errorCode, errorString, advancedOrderRejectJson=""): # 注意:TWS API 会把通知、警告和错误都放到 error 回调里。 print(f"API message: reqId={reqId}, code={errorCode}, message={errorString}")
app = App()app.connect("127.0.0.1", 7497, clientId=1)
# Python API 需要启动 run() 循环处理从 TWS 返回的消息。thread = threading.Thread(target=app.run, daemon=True)thread.start()
if not app.ready.wait(8): app.disconnect() raise TimeoutError("连接已发起,但没有收到 nextValidId 回调")
if not app.done.wait(8): app.disconnect() raise TimeoutError("已连接,但 reqCurrentTime 没有返回")
print(f"SERVER_TIME={app.server_time}")
time.sleep(0.2)thread.join(timeout=1)这段代码里有两个重点:
App同时继承EWrapper和EClient:EClient负责发请求,EWrapper负责接回调。app.run()必须运行:否则 TWS 返回的消息进了队列,但你的程序不会处理回调。
成功输出示例
Section titled “成功输出示例”使用 TWS 模拟账户、127.0.0.1:7497 和独立 clientId 检查,连接和 reqCurrentTime() 正常时,会看到类似输出:
TWS API OK: server_time=1781368619, host=127.0.0.1, port=7497server_time 是 Unix 时间戳。它能返回,说明 Python API 包、TWS Socket 端口、nextValidId()、reqCurrentTime() 和回调处理链路都已经通了。
常见连接错误
Section titled “常见连接错误”| 现象 | 常见原因 | 排查方向 |
|---|---|---|
502 Couldn't connect to TWS | TWS 没开、API 没启用、端口不一致、防火墙拦截 | 先看 TWS 是否登录,再核对 API Settings 端口 |
一直收不到 nextValidId | socket 打开了,但握手没有完成,或 TWS 弹出确认窗口未接受 | 切回 TWS 查看是否有连接确认;如果之前已经信任过同一台电脑的连接,可能不会再弹 |
收到 501 Already Connected | 同一个客户端对象重复连接 | 不要对同一个 App 实例重复 connect() |
收到 504 Not connected | 还没连接成功就发请求,或连接已经断开 | 等 nextValidId 后再发请求 |
| 一连接就断开 | clientId 冲突、TWS 关闭、端口变化或网络中断 | 换一个 clientId,检查 TWS 日志和 API 设置 |
其中 502 是客户端本地错误,不一定会出现在 TWS 日志里。遇到它时,先检查 TWS / IB Gateway 是否正在监听对应端口。