跳转到内容

TWS 架构

Trader Workstation 通常简称 TWS,是 IBKR 的完整桌面交易客户端。使用 TWS API 时,开发者的程序不是直接连交易所,也不是直接连 IBKR 后端,而是先通过 Socket 连接已经登录的 TWS。

可以把 TWS 理解成一个带图形界面的本地交易网关:它负责登录、认证、行情权限、订单路由、账户会话和风险提示;你的程序负责发送 API 请求,并处理 TWS 返回的回调。

TWS API 的典型结构如下:

Python / Java / C# / C++ 程序
|
| TCP Socket
| 模拟账户常见端口 7497,真实账户常见端口 7496
v
Trader Workstation
|
| 登录、认证、行情权限、订单路由、账户会话
v
IBKR 后端服务
|
v
交易所、行情源、清算和账户系统

因此,排查 TWS API 问题时,不能只看代码。TWS 是否已经登录、API 端口是否启用、账户是否有权限、交易时段是否允许、订单预防是否触发,都会影响最终结果。

官方 TWS API 的常见代码结构由三个概念组成:

组件作用新手理解
EClient发送请求代码通过它调用 reqMktData()placeOrder()reqContractDetails() 等方法
EWrapper接收回调TWS 把行情、订单状态、错误码、账户数据回传到这些方法里
EReader / app.run()读取消息持续从 Socket 读取 TWS 返回的数据,并分发给 EWrapper

在 Python API 里,常见写法是让同一个类同时继承 EWrapperEClient

from ibapi.client import EClient
from 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 不是“发一个请求,立刻返回一个结果”的同步模型。大多数接口都是:

  1. EClient 发出请求。
  2. TWS 接收请求并转发给 IBKR 后端或本地模块。
  3. 结果通过 EWrapper 的回调方法异步返回。

例如请求合约详情时,代码会调用:

app.reqContractDetails(reqId, contract)

结果不会直接从这一行返回,而是进入:

def contractDetails(self, reqId, contractDetails):
print(contractDetails.contract.symbol)
def contractDetailsEnd(self, reqId):
print("合约详情返回结束")

理解这个异步回调模型很重要。很多新手以为 reqContractDetails() 后面一行就能拿到结果,实际应该在回调方法里接收和保存结果。

TWS 在 API 架构里承担这些职责:

职责说明
登录和认证API 程序复用 TWS 已登录的账户会话
Socket 监听按设置页端口接收 API 程序连接
权限检查行情、交易、账户和产品权限都会经过检查
订单预防下单前可能触发价格、数量、保证金、交易时段等保护
消息分发把行情、订单、账户、错误码等结果回调给程序
日志记录生成 API 日志和平台日志,方便排查问题

开发者代码主要负责构造请求、发送请求、保存回调结果和处理异常。是否允许下单、是否有行情权限、订单是否能路由,最终仍由 TWS 和 IBKR 后端规则决定。

连接 TWS 时通常需要三个参数:

参数含义常见写法
hostTWS 所在机器的地址同一台电脑运行时通常是 127.0.0.1
portTWS API 监听端口模拟账户常见 7497,真实账户常见 7496
clientIdAPI 客户端编号多个程序同时连接时必须区分

示例:

host = "127.0.0.1"
port = 7497
client_id = 910

如果程序和 TWS 不在同一台机器上,还要额外检查 TWS 设置页里的连接权限、可信 IP、防火墙和网络路由。

TWS API 支持多个 API 客户端连接同一个 TWS,但每个客户端应该使用不同的 clientId

clientId 的作用包括:

  • 区分不同程序发出的请求。
  • 区分不同程序收到的订单状态和回调。
  • 避免两个程序共用同一个编号造成状态混乱。

如果只是学习和测试,可以固定使用一个不容易冲突的编号,例如 9101001。如果是正式工具,建议给不同模块分配清晰的编号范围。

TWS 有完整图形界面,适合学习和开发早期阶段:

  • 可以直接看到账户、行情、订单和风险提示。
  • 可以人工核对 API 设置是否生效。
  • 可以看到订单预防弹窗和拒单原因。
  • 可以使用模拟账户降低测试风险。
  • 出错时更容易把代码回调、TWS 提示和日志对应起来。

等 API 设置、连接、合约、行情、历史数据和订单流程都跑通后,再迁移到 IB Gateway 会更顺。

TWS 和 IB Gateway 都可以作为 TWS API 的 Socket 连接端。它们使用同一套 API 思路,但定位不同:

选择更适合
TWS学习、开发、人工确认、图形界面教程、订单状态核对
IB Gateway服务器运行、长期后台进程、自动化服务

从代码层面看,很多示例只需要调整端口就能从 TWS 切换到 IB Gateway。但从运行维护角度看,登录、重新认证、进程守护、日志和网络安全都需要重新设计。

官方 TWS API 文档入口:

TWS API Documentation