跳转到内容

实现 Visual Basic .NET

Visual Basic .NET 不是 TWS API 新项目的首选路线,但官方 API 包仍然提供了 VB.NET 示例。理解这一页的关键是:VB.NET 示例不是直接使用一个独立的 VB API,而是通过引用官方 C# 客户端库 CSharpAPI 来访问 IBApi 命名空间。

如果你只是从零开始做自动化交易系统,优先选择 Python、Java 或 C# 会更省事;如果你的团队已有 VB.NET、WinForms、桌面工具或旧系统代码,那么可以按这里的方式接入。

官方 VB.NET 示例在 MainModule.vb 里明确建议:以前使用 ActiveX 控件的 VB.NET 用户,应改用 C# client library,也就是 CSharpAPI

这句话很重要,因为很多旧资料会把 VB.NET、ActiveX、VBA 混在一起:

名称适用场景建议
VB.NETVisual Studio 里的 .NET Framework 项目可以引用 CSharpAPI 项目或编译后的 DLL
ActiveX / COM更老的 Windows 集成方式新项目不建议继续作为主线
Excel VBAExcel 宏环境不等同于 VB.NET,工程结构和依赖方式不同
C# API 库官方 .NET 客户端库VB.NET 示例实际依赖它

因此,VB.NET 接入 TWS API 的核心不是寻找 VBApi.dll,而是在 VB.NET 工程里引用 CSharpAPI.csproj 或它编译出的 CSharpAPI.dll,然后使用 IBApi.EClientSocketIBApi.EWrapperIBApi.EReader 这些类。

安装 TWS API 后,官方 VB.NET 示例通常位于 API 包的 samples/VB 目录下。以常见目录结构为例:

文件或目录作用
samples/VB/Testbed/VB_Testbed.slnVB.NET 控制台示例解决方案
samples/VB/Testbed/Testbed.vbprojVB.NET 示例工程,目标框架为 .NET Framework 4.8
samples/VB/Testbed/MainModule.vb示例入口,包含连接 TWS、启动消息线程、等待 nextValidId 的主流程
samples/VB/Testbed/EWrapperImpl.vbEWrapper 回调实现,接收 nextValidId、行情、订单、账户等回调
source/CSharpClient/client/CSharpAPI.csproj官方 C# API 客户端库,VB.NET 工程通过项目引用使用它
samples/VB/VB_API_Sample/VB_API_sample.sln更完整的 WinForms 示例,界面和代码量较大

学习时先看 Testbed,它更适合理解连接链路;VB_API_Sample 更像完整桌面程序示例,适合已经熟悉 TWS API 后参考界面组织方式。

VB.NET 和 C#、Python 的底层逻辑相同:先连接 TWS 或 IB Gateway,再由后台读取线程处理服务端回调。

典型流程如下:

  1. 在 TWS 或 IB Gateway 中启用 API Socket。
  2. 确认端口:模拟账户 TWS 常用 7497,真实账户 TWS 常用 7496
  3. VB.NET 工程引用 CSharpAPI
  4. 创建 EWrapper 实现类,用来接收回调。
  5. 创建 EClientSocket 并调用 eConnect()
  6. 启动 EReader 消息线程。
  7. 等待 nextValidId 回调,确认连接已经建立。
  8. 再发送行情、账户、订单等请求。

nextValidId 经常被新手忽略。它不仅是下单编号的起点,也可以作为连接建立后的第一个重要信号:如果一直收不到它,通常说明 Socket 没真正连通、端口错误、API 没启用,或 TWS 阻止了这个客户端连接。

下面的片段对应官方 Testbed 示例的连接骨架。它依赖同一工程中的 EWrapperImpl.vb,不是单文件脚本。

Imports IBApi
Imports System.Threading
Module MainModule
Dim wrapperImpl As EWrapperImpl = New EWrapperImpl
Sub Main()
' EClientSocket 负责向 TWS / IB Gateway 发送请求。
Dim socketClient As EClientSocket = wrapperImpl.socketClient
' 连接同一台机器上的 TWS 模拟账户。真实账户通常改为 7496。
socketClient.eConnect("127.0.0.1", 7497, 0)
' EReader 负责从 Socket 队列中读取服务端消息。
Dim msgThread As Thread = New Thread(AddressOf messageProcessing)
msgThread.IsBackground = True
If wrapperImpl.serverVersion() > 0 Then
msgThread.Start()
End If
' 等待 nextValidId 回调。收到后说明连接链路已经可用。
While wrapperImpl.nextOrderId = 0
Thread.Sleep(100)
End While
' 发送一个简单请求:获取服务器时间。
socketClient.reqCurrentTime()
Thread.Sleep(1000)
socketClient.eDisconnect()
End Sub
Private Sub messageProcessing()
Dim reader As EReader = New EReader(wrapperImpl.socketClient, wrapperImpl.eReaderSignal)
reader.Start()
While wrapperImpl.socketClient.IsConnected
wrapperImpl.eReaderSignal.waitForSignal()
reader.processMsgs()
End While
End Sub
End Module
对象或参数中文解释
EClientSocket客户端请求入口。调用 reqCurrentTime()reqMktData()placeOrder() 等方法时,都是通过它发送到 TWS。
EWrapper回调接口。TWS 返回的数据不会直接从请求函数返回,而是进入 EWrapper 的对应回调方法。
EWrapperImpl示例工程里自己写的回调实现类,负责保存 socketClienteReaderSignalnextOrderId 等状态。
EReader消息读取器。连接后必须持续处理消息队列,否则回调不会正常进入程序。
eConnect(host, port, clientId)建立连接。host 通常是 127.0.0.1port 要和 TWS API 设置一致,clientId 用来区分不同 API 客户端。
nextValidId(orderId)连接成功后常见的回调之一,也是后续下单编号的起点。
reqCurrentTime()获取服务器时间的简单请求,适合用来验证回调链路。

官方 Testbed.vbproj 使用的是 .NET Framework 4.8,并引用 source/CSharpClient/client/CSharpAPI.csproj。在 Visual Studio 中打开时,要确认以下几点:

  • 解决方案能找到 CSharpAPI.csproj,否则会出现项目引用丢失。
  • PlatformTarget 与依赖一致。官方示例常见为 x86
  • Google.Protobuf 等依赖要能还原或从示例包路径中找到。
  • TWS 或 IB Gateway 已登录,并且 API Socket 已启用。
  • hostportclientId 与运行环境一致。

如果你只是想验证 TWS 是否能连通,Python 的 currentTime 脚本通常更快;VB.NET 更适合已有 .NET 桌面工程需要集成 IBKR 交易能力的场景。

为什么 VB.NET 示例还要引用 C# 项目?

Section titled “为什么 VB.NET 示例还要引用 C# 项目?”

因为官方 .NET 客户端库主要以 C# 项目形式提供。VB.NET 可以引用 C# 项目或 DLL,然后调用同一个 IBApi 命名空间。这样比继续维护 ActiveX / COM 集成更清晰。

可以直接把示例代码复制进自己的工程吗?

Section titled “可以直接把示例代码复制进自己的工程吗?”

可以参考结构,但不要只复制 MainModule.vb。它依赖 EWrapperImpl.vb、工程引用、回调实现和 NuGet/本地 DLL 依赖。更稳妥的做法是先让官方 Testbed 能编译,再把连接、回调、请求代码迁移到自己的工程。

优先检查四件事:

  • TWS API 设置中是否启用了 Socket 客户端。
  • 端口是否匹配模拟账户或真实账户环境。
  • EReader 消息线程是否已经启动。
  • EWrapperImpl.nextValidId() 是否收到回调并保存了 nextOrderId

如果 eConnect() 没报错,但 nextValidId 一直不出现,重点看 TWS 端口、API 设置、连接限制和客户端 ID 冲突。