跳转到内容

C++、C# 和 Java 实现

C++、C# 和 Java 的 TWS API 连接结构,比 Python 更明显地把 EReader 和消息处理循环拆开。

核心模式是:

创建 EReader -> reader.start() -> 等待 signal -> reader.processMsgs()

reader.start() 负责启动读取线程,把 socket 中的消息放入消息队列。reader.processMsgs() 负责从队列里取消息、解码并触发 EWrapper 回调。

Python 常见写法是启动 app.run()

threading.Thread(target=app.run, daemon=True).start()

C++、C# 和 Java 官方示例更常见的结构是自己创建 EReader,然后在后台线程里等待信号并调用 processMsgs()

语言读取线程处理消息
PythonEReader 在连接后由客户端创建app.run() 从队列取消息并解码
Java显式创建 EReader等待 EReaderSignal 后调用 reader.processMsgs()
C#显式创建 EReader等待 EReaderSignal 后调用 reader.processMsgs()
C++显式创建 EReader等待 EReaderOSSignal 后调用 processMsgs()

它们的共同点是:连接以后必须持续处理返回消息。否则请求发出去了,回调不会正常到达。

Java 官方示例常见结构如下:

// 创建连接客户端时传入 EWrapper 和 EReaderSignal。
EReaderSignal signal = new EJavaSignal();
EClientSocket client = new EClientSocket(wrapper, signal);
client.eConnect("127.0.0.1", 7497, 1);
// EReader 负责从 TWS / IB Gateway 读取消息。
EReader reader = new EReader(client, signal);
reader.start();
// 这个线程负责等待信号并处理消息队列。
new Thread(() -> {
while (client.isConnected()) {
signal.waitForSignal();
try {
reader.processMsgs();
} catch (Exception e) {
System.out.println("处理 TWS API 消息失败: " + e.getMessage());
}
}
}).start();

这里的 waitForSignal() 可以理解为“等待读取线程告诉我有新消息了”。processMsgs() 才是真正触发 EWrapper 回调的地方。

C# 官方示例也使用类似模式:

// signal 用来通知消息队列里有新数据。
EReaderSignal signal = new EReaderMonitorSignal();
EClientSocket clientSocket = new EClientSocket(wrapper, signal);
clientSocket.eConnect("127.0.0.1", 7497, 1);
var reader = new EReader(clientSocket, signal);
reader.Start();
new Thread(() =>
{
while (clientSocket.IsConnected())
{
signal.waitForSignal();
reader.processMsgs();
}
})
{
IsBackground = true
}.Start();

如果 reader.processMsgs() 这段循环没有启动,nextValidId()error()tickPrice()historicalData() 等回调都不会按预期触发。

C++ 示例中常见的是 EReaderOSSignal

EReaderOSSignal osSignal(2000);
EClientSocket client(wrapper, &osSignal);
client.eConnect("127.0.0.1", 7497, 1);
std::unique_ptr<EReader> reader(new EReader(&client, &osSignal));
reader->start();
while (client.isConnected()) {
osSignal.waitForSignal();
reader->processMsgs();
}

EReaderOSSignal 的作用仍然是通知主循环:有新的消息可以处理。真正触发回调的是 reader->processMsgs()

无论使用哪种语言,都不建议在 eConnect() 后立刻发送大量业务请求。更稳的方式是:

  1. 连接 TWS / IB Gateway。
  2. 启动 EReader
  3. 启动 processMsgs() 循环。
  4. 等到 nextValidId() 回调。
  5. 再开始请求行情、合约、账户或提交订单。

如果没有等 nextValidId(),程序可能在初始握手还没完成时就发送业务请求,表现为无回调、Not connected、订单 ID 异常或偶发失败。

在 C++、C# 和 Java 中,建议把线程职责分清楚:

线程或任务职责
UI 线程只负责界面显示,不直接阻塞 API 消息循环
EReader 线程从 socket 读取消息
processMsgs 循环解码消息并触发回调
业务工作线程写数据库、策略计算、前端推送、批量任务

不要在 processMsgs() 所触发的回调里做长时间阻塞操作。回调里只做轻量处理,把重活交给业务队列。

现象常见原因
连接成功但没有任何回调reader.processMsgs() 没有持续运行
只收到部分回调回调里做了耗时任务,消息处理被堵住
UI 卡死把 API 消息循环放在 UI 线程里直接跑
断线后无法恢复旧的 reader 线程和 signal 没有清理,重连逻辑重复创建对象
多客户端混乱多个程序使用相同 clientId 或共享同一套状态

IBKR Campus: TWS API Documentation