TWS 架构
Trader Workstation 通常简称 TWS,是 IBKR 的完整桌面交易客户端。使用 TWS API 时,开发者的程序不是直接连交易所,也不是直接连 IBKR 后端,而是先通过 Socket 连接已经登录的 TWS。
可以把 TWS 理解成一个带图形界面的本地交易网关:它负责登录、认证、行情权限、订单路由、账户会话和风险提示;你的程序负责发送 API 请求,并处理 TWS 返回的回调。
TWS API 的典型结构如下:
Python / Java / C# / C++ 程序 | | TCP Socket | 模拟账户常见端口 7497,真实账户常见端口 7496 vTrader Workstation | | 登录、认证、行情权限、订单路由、账户会话 vIBKR 后端服务 | v交易所、行情源、清算和账户系统因此,排查 TWS API 问题时,不能只看代码。TWS 是否已经登录、API 端口是否启用、账户是否有权限、交易时段是否允许、订单预防是否触发,都会影响最终结果。
官方 TWS API 的常见代码结构由三个概念组成:
| 组件 | 作用 | 新手理解 |
|---|---|---|
EClient | 发送请求 | 代码通过它调用 reqMktData()、placeOrder()、reqContractDetails() 等方法 |
EWrapper | 接收回调 | TWS 把行情、订单状态、错误码、账户数据回传到这些方法里 |
EReader / app.run() | 读取消息 | 持续从 Socket 读取 TWS 返回的数据,并分发给 EWrapper |
在 Python API 里,常见写法是让同一个类同时继承 EWrapper 和 EClient:
from ibapi.client import EClientfrom ibapi.wrapper import EWrapper
class App(EWrapper, EClient): def __init__(self): # 把 EWrapper 自己传给 EClient。 # 这样请求结果才能回到这个类里的回调方法。 EClient.__init__(self, self)
def nextValidId(self, orderId: int): # TWS 连接成功后会返回可用订单 ID。 # 后面下单时通常从这个 ID 开始递增。 print("下一个可用订单 ID:", orderId)这段代码只展示架构关系。真正运行时还需要 connect()、消息循环线程、等待回调和 disconnect(),连接章节会继续展开。
TWS API 不是“发一个请求,立刻返回一个结果”的同步模型。大多数接口都是:
- 用
EClient发出请求。 - TWS 接收请求并转发给 IBKR 后端或本地模块。
- 结果通过
EWrapper的回调方法异步返回。
例如请求合约详情时,代码会调用:
app.reqContractDetails(reqId, contract)结果不会直接从这一行返回,而是进入:
def contractDetails(self, reqId, contractDetails): print(contractDetails.contract.symbol)
def contractDetailsEnd(self, reqId): print("合约详情返回结束")理解这个异步回调模型很重要。很多新手以为 reqContractDetails() 后面一行就能拿到结果,实际应该在回调方法里接收和保存结果。
TWS 负责什么
Section titled “TWS 负责什么”TWS 在 API 架构里承担这些职责:
| 职责 | 说明 |
|---|---|
| 登录和认证 | API 程序复用 TWS 已登录的账户会话 |
| Socket 监听 | 按设置页端口接收 API 程序连接 |
| 权限检查 | 行情、交易、账户和产品权限都会经过检查 |
| 订单预防 | 下单前可能触发价格、数量、保证金、交易时段等保护 |
| 消息分发 | 把行情、订单、账户、错误码等结果回调给程序 |
| 日志记录 | 生成 API 日志和平台日志,方便排查问题 |
开发者代码主要负责构造请求、发送请求、保存回调结果和处理异常。是否允许下单、是否有行情权限、订单是否能路由,最终仍由 TWS 和 IBKR 后端规则决定。
常见连接参数
Section titled “常见连接参数”连接 TWS 时通常需要三个参数:
| 参数 | 含义 | 常见写法 |
|---|---|---|
host | TWS 所在机器的地址 | 同一台电脑运行时通常是 127.0.0.1 |
port | TWS API 监听端口 | 模拟账户常见 7497,真实账户常见 7496 |
clientId | API 客户端编号 | 多个程序同时连接时必须区分 |
示例:
host = "127.0.0.1"port = 7497client_id = 910如果程序和 TWS 不在同一台机器上,还要额外检查 TWS 设置页里的连接权限、可信 IP、防火墙和网络路由。
多客户端连接
Section titled “多客户端连接”TWS API 支持多个 API 客户端连接同一个 TWS,但每个客户端应该使用不同的 clientId。
clientId 的作用包括:
- 区分不同程序发出的请求。
- 区分不同程序收到的订单状态和回调。
- 避免两个程序共用同一个编号造成状态混乱。
如果只是学习和测试,可以固定使用一个不容易冲突的编号,例如 910、1001。如果是正式工具,建议给不同模块分配清晰的编号范围。
适合先用 TWS 的原因
Section titled “适合先用 TWS 的原因”TWS 有完整图形界面,适合学习和开发早期阶段:
- 可以直接看到账户、行情、订单和风险提示。
- 可以人工核对 API 设置是否生效。
- 可以看到订单预防弹窗和拒单原因。
- 可以使用模拟账户降低测试风险。
- 出错时更容易把代码回调、TWS 提示和日志对应起来。
等 API 设置、连接、合约、行情、历史数据和订单流程都跑通后,再迁移到 IB Gateway 会更顺。
和 IB Gateway 的关系
Section titled “和 IB Gateway 的关系”TWS 和 IB Gateway 都可以作为 TWS API 的 Socket 连接端。它们使用同一套 API 思路,但定位不同:
| 选择 | 更适合 |
|---|---|
| TWS | 学习、开发、人工确认、图形界面教程、订单状态核对 |
| IB Gateway | 服务器运行、长期后台进程、自动化服务 |
从代码层面看,很多示例只需要调整端口就能从 TWS 切换到 IB Gateway。但从运行维护角度看,登录、重新认证、进程守护、日志和网络安全都需要重新设计。
官方 TWS API 文档入口: