通过 TWS 远程连接 TWS API
远程连接的意思是:TWS 运行在一台机器上,API 程序运行在另一台机器上。程序不再连接 127.0.0.1,而是连接 TWS 所在机器的局域网 IP、内网 IP 或经过安全隧道暴露出来的地址。
典型链路如下:
远程 API 程序 -> 网络 -> 运行 TWS 的机器 -> TWS Socket 端口 -> IBKR 后端远程连接能用,但不适合作为新手的第一步。建议先把同一台电脑上的 127.0.0.1:7497 跑通,再改成远程地址。否则一旦失败,很难判断是 TWS 设置、网络、防火墙、IP 白名单、端口还是代码本身的问题。
什么时候需要远程连接
Section titled “什么时候需要远程连接”| 场景 | 是否适合 | 说明 |
|---|---|---|
| 同一台电脑上的脚本连接 TWS | 更适合新手 | 使用 127.0.0.1,变量最少 |
| 局域网内另一台电脑连接 TWS | 可以使用 | 需要 Trusted IPs 和防火墙放行 |
| 服务器连接家里或办公室电脑上的 TWS | 谨慎使用 | 网络不稳定、IP 变化和安全风险更高 |
| 公开互联网直接暴露 TWS API 端口 | 不建议 | TWS API Socket 不应当当作公网 Web 接口使用 |
| 服务器长期运行自动化程序 | 更建议用 IB Gateway | IB Gateway 界面更轻,更适合服务端会话 |
如果只是让网页前端直接连接用户电脑上的 TWS,这条路不适合。浏览器不能原生连接 TWS API 的 TCP Socket,真正需要网页界面时,应由本地脚本或服务器后端作为中间层。
TWS 端需要改什么
Section titled “TWS 端需要改什么”在运行 TWS 的机器上,进入:
Global Configuration -> API -> Settings远程连接通常要确认这些设置:
| 设置 | 建议 | 说明 |
|---|---|---|
Enable ActiveX and Socket Clients | 勾选 | 启用 TWS API Socket |
Socket Port | 记录实际端口 | TWS 模拟账户常见为 7497,真实账户常见为 7496 |
Read-Only API | 按用途决定 | 只读测试可勾选;需要测试订单或 WhatIf 时不能只读 |
Allow connections from localhost only | 远程连接时不能只允许本地连接 | 只允许本地连接时,外部机器无法连接 |
Trusted IPs | 只添加需要连接的单个 IP | 不要放宽到不确定的地址范围 |
官方文档说明,远程连接需要在 Trusted IPs 中配置允许连接的 IP;这里填写的是远程 API 程序所在机器的 IP,不是 TWS 机器自己的 IP。
IP 应该填哪一个
Section titled “IP 应该填哪一个”假设:
- TWS 运行在 Windows 电脑:
192.168.1.20 - Python 程序运行在另一台电脑:
192.168.1.31 - TWS API 端口:
7497
那么配置关系是:
| 位置 | 应该填写 |
|---|---|
TWS 的 Trusted IPs | 192.168.1.31 |
Python 的 host | 192.168.1.20 |
Python 的 port | 7497 |
很多连接失败来自把这两个 IP 写反:Trusted IPs 写的是“谁被允许连接我”,代码里的 host 写的是“我要连接谁”。
Windows 防火墙
Section titled “Windows 防火墙”只改 TWS API 设置还不够。运行 TWS 的电脑还必须允许入站连接到对应端口。
常见做法是:
- 只在专用网络或内网环境中放行。
- 只放行 TWS API 使用的端口,例如
7497。 - 只允许可信来源 IP 访问,不要对公网开放。
如果 TWS 设置正确,但远程程序仍然报连接失败,先检查网络是否能访问这台机器和端口,再看 Python 代码。端口不通时,改 EWrapper 回调没有帮助。
Python 连接示例
Section titled “Python 连接示例”远程连接代码和本地连接代码几乎一样,唯一变化是 host 不再是 127.0.0.1。
import threading
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,说明远程 socket 已经完成 API 握手。 self.ready.set() self.reqCurrentTime()
def currentTime(self, time_: int): # time_ 是 IBKR 服务器时间戳,用来验证请求和回调链路。 self.server_time = time_ self.done.set() self.disconnect()
def error(self, reqId, errorCode, errorString, advancedOrderRejectJson=""): print(f"API message: reqId={reqId}, code={errorCode}, message={errorString}")
app = App()
# 改成运行 TWS 的那台机器的地址。TWS_HOST = "192.168.1.20"TWS_PORT = 7497CLIENT_ID = 21
app.connect(TWS_HOST, TWS_PORT, clientId=CLIENT_ID)
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}")thread.join(timeout=1)这段示例只验证连接和 reqCurrentTime(),不请求行情、不读账户、不下单。远程连接第一次调试时,先用这种最小请求确认链路,再继续写业务接口。
远程连接的安全边界
Section titled “远程连接的安全边界”TWS API 是给受信任的本地程序或内网程序使用的交易接口,不应该像普通网站 API 一样裸露在公网。
建议按下面优先级选择:
| 优先级 | 方式 | 说明 |
|---|---|---|
| 1 | 同一台电脑 127.0.0.1 | 最简单,适合开发和连接检查 |
| 2 | 局域网固定 IP | 适合两台可信设备在同一网络中调试 |
| 3 | VPN 或 SSH 隧道 | 适合跨网络,但仍把 TWS 端口限制在受控通道内 |
| 4 | 公网直接开放端口 | 不建议 |
如果要做网站或后端服务,更合理的结构是:网站后端连接服务器上的 IB Gateway,或者用户本地安装连接器,由连接器和 TWS 通信。不要让网页前端直接承担 TWS Socket 连接职责。
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 远程连接直接失败 | TWS 只允许 localhost | 检查 Allow connections from localhost only |
| 本地连接能连,另一台机器不能连 | Trusted IPs 没加对,或防火墙拦截 | 确认远程程序所在机器 IP |
| 连接后一直没有回调 | TWS 弹窗未接受、消息循环没启动、网络中断 | 看 TWS 界面和 app.run() 是否运行 |
| 改了 IP 仍然连不上 | 写成了 TWS 机器自己的 IP,或 IP 已变化 | 重新确认两台机器的实际内网 IP |
| 远程偶发断开 | Wi-Fi、休眠、TWS 重启、认证过期 | 程序要设计重连和状态恢复 |
远程连接失败时,先回到本地连接检查。如果 TWS 所在机器上使用 127.0.0.1 也连不上,问题通常不在远程网络,而在 TWS API 设置、端口或 Python 环境。