【TWS API 翻译系列】6、TWS API的体系结构和连接
作者:yunjinqi   类别:    日期:2022-01-03 22:09:01    阅读:1185 次   消耗积分:0 分    

对 API 进行编程 :体系结构

EClientSocket 和 EWrapper Classes

一旦TWS启动运行并主动监听传入的连接,我们就可以编写代码了。这就把我们带到了TWS API的两个主要类:IBApi.EWrapper接口和IBApi.EClientSocket。

实现 EWrapper 接口

IBApi.EWrapper接口是 TWS 向 API 客户端应用程序传递信息的机制。通过实现此接口,客户端应用程序将能够接收和处理来自TWS的信息。有关如何实现接口的更多信息,请参阅编程语言的文档。

 class TestWrapper(wrapper.EWrapper):

电子客户端会话类

用于向TWS发送消息的类是IBApi.EClientSocket。与 EWrapper 不同,此类不会被重写,因为 EClientSocket 中提供的函数被调用以将消息发送到 TWS。要使用 EClientSocket,首先可能需要将IBApi.EWrapper接口实现为其构造函数参数的一部分,以便应用程序可以处理所有返回的消息。从TWS发送的消息作为对IBApi.EClientSocket中函数调用的响应需要EWrapper实现,以便可以处理它们以满足API客户端的需求。

另一个关键元素是传递给EClientSocket的构造函数的IBApi.EReaderSignal对象。除 Python 外,此对象在 API 中用于指示消息已准备好在队列中进行处理。(在 Python 中,Queue 类直接处理此任务)。我们将在ereader线程部分中更详细地讨论此对象。

class TestClient(EClient):
         def __init__(self, wrapper):
             EClient.__init__(self, wrapper)
             
class TestApp(TestWrapper, TestClient):
         def __init__(self):
             TestWrapper.__init__(self)
             TestClient.__init__(self, wrapper=self)

注意:EReaderSignal 类不用于 Python API。Python 队列模块用于线程间通信和数据交换。

连接

API 客户端应用程序和 TWS 之间的socket连接是使用IBApi.EClientSocket.eConnect函数建立的。TWS 充当服务器来接收来自 API 应用程序(客户端)的请求,并通过采取适当的操作进行响应。第一步是 API 客户端在 TWS 已在监听的socket端口上启动与 TWS 的连接。如果每个实例配置了不同的 API socket端口号,则可以在同一台计算机上运行多个 TWS 实例。此外,每个TWS会话可以同时接收多达32个不同的客户端应用程序。API 连接中指定的客户端 ID字段用于区分不同的 API 客户端。

建立 API 连接

一旦创建了我们的两个主要对象,EWrapper和ESocketClient,客户端应用程序就可以通过IBApi.EClientSocket对象进行连接:

app.connect("127.0.0.1", args.port, clientId=0)

eConnect 首先从操作系统请求将 TCP socket 打开到指定的 IP 地址和socket端口。如果无法打开socket,操作系统(不是TWS)会将 API 客户端收到的错误作为错误代码 502 返回到IBApi.EWrapper.error(注意:由于此错误不是由 TWS 生成的,因此不会在 TWS 日志文件中捕获)。最常见的错误 502 将指示 TWS 未在启用 API 的情况下运行,或者它正在监听其他socket端口上的连接。如果通过网络连接,则当防火墙或防病毒程序阻止连接,或者路由器的 IP 地址未在 TWS 的"受信任的 IP"中列出时,也会发生此错误。

打开socket后,必须进行初始握手,其中交换有关TWS和API支持的最高版本的信息。这一点很重要,因为 API 消息在不同的版本中可以具有不同的长度和字段,并且必须具有版本号才能正确解释收到的消息。

  • 因此,在建立连接之前,不要创建主ereader对象,这一点很重要。初始连接会在TWS和API客户端之间产生协商的通用版本,ereader线程在解释后续消息时需要该版本。

在建立可用于通信的最高版本号后,TWS 将返回与已登录的 TWS 用户会话特别对应的某些数据片段。这包括 (1) 在此 TWS 会话中可访问的帐号,(2) 下一个有效的订单标识符 (ID) 和 (3) 连接时间。在最常见的操作模式下,EClient.AsyncEConnect 字段设置为 false,并且在建立socket连接后立即完成初始握手。然后,TWS 将立即向 API 客户端提供此信息。

  • 重要提示: IBApi.EWrapper.nextValidID回调通常用于指示连接已完成,并且可以将其他消息从 API 客户端发送到 TWS。在此时间之前进行的函数调用可能会被 TWS 删除。

在特殊情况下,有一种替代的、已弃用的连接模式,其中变量 AsyncEconnect 设置为 true,并且仅从 connectAck() 函数调用 startAPI。所有 IB 样本都使用模式 AsyncEconnect = False。

ereader线程

API 程序始终至少有两个执行线程。一个线程用于将消息发送到 TWS,另一个线程用于读取返回的消息。第二个线程使用 API EReader 类从socket读取消息并将消息添加到队列中。每次将新消息添加到消息队列时,都会触发一个通知标志,以便现在有消息等待处理,让其他线程进入。在 API 程序的双线程设计中,消息队列也由第一个线程处理。在三线程设计中,将创建一个附加线程来执行此任务。负责消息队列的线程将解码消息并调用 EWrapper 中的相应函数。双线程设计用于 IB Python 示例 Program.py 和C++示例 TestCppClient,而其他语言中的"Testbed"示例使用三线程设计。通常在Python异步网络应用程序中,asyncio模块将用于创建更具顺序性的代码设计。

具有读取和分析来自TWS的原始消息的功能的类是IBApi.EReader类。

在Python IB API中,下面的代码包含在Client::connect()中,因此ereader线程在连接时自动启动。用户无需启动reader。

 # You don't need to run this in your code!
  self.reader = reader.EReader(self.conn, self.msg_queue)
  self.reader.start()   # start thread

连接客户端后,将自动创建一个reader线程来处理传入的消息,并将消息放入消息队列中以进行进一步处理。用户需要在下面触发 Client::run() ,其中消息队列在无限循环中处理,并且自动触发 EWrapper 回调函数。

app.run()

现在是时候重新审视IBApi.EReaderSignal最初在EClientSocket类中引入的角色了。如上一段所述,在ereader线程将消息放入队列后,将发出通知以表明消息已准备好进行处理。在(C++,C#/.NET,Java)API中,这是通过我们在IBApi.EWrapper的实现器中启动的IBApi.EReaderSignal对象完成的。在 Python API 中,它由 Queue自动处理。

客户端应用程序现在已准备好与交易者工作站配合使用!连接完成后,API程序将开始接收诸如IBApi.EWrapper.nextValidIdIBApi.EWrapper.managedAccounts之类的事件。在TWS(不是IB网关)中,如果有一个活动的网络连接,也会立即回调到IBApi::EWrapper::error,errorId为-1,errorCode= 2104,2106,errorMsg = “市场数据服务器正常”,以指示存在与IB市场数据服务器的活动连接。对IBApi::EWrapper::error的回调,如果 errorId 为 -1,则不代表真正的"错误",而仅表示已成功连接到 IB 市场数据场的通知。

相比之下,IB网关在IB客户端发出请求之前不会与市场数据场建立连接。在此之前,IB 网关 GUI 中的连接指示器将显示黄色的"非活动",而不是"活动"的绿色指示。

最初从 API 应用程序发出请求时,重要的是要验证是否收到了响应,而不是假设网络连接正常且订阅请求(产品组合更新、帐户信息等)已成功进行。

接受来自 TWS 的 API 连接

出于安全原因,默认情况下,API 未配置为自动接受来自 API 应用程序的连接请求。尝试连接后,TWS 中将出现一个对话框,要求用户手动确认是否可以建立连接:

conn_prompt.png

为了防止TWS要求最终用户接受连接,可以将其配置为自动接受来自受信任IP地址和/或本地计算机的连接。这可以通过TWS API设置轻松完成:

tws_allow_connections.png

注意:在尝试向TWS执行任何请求之前,您必须确保连接已完全建立。如果不这样做,将导致TWS关闭连接。通常,这可以通过等待事件的回调和初始连接握手的结束来完成,例如IBApi.EWrapper.nextValidIdIBApi.EWrapper.managedAccounts

在极少数情况下,IB网关或TWS在建立连接到IB服务器时有短暂的延迟,在收到nextValidId后立即发送的消息可能会被删除,并且需要重新发送。如果 API 客户端尚未收到来自已发出请求的预期回调,则不应继续进行排序,因为连接是否正常。

API socket连接断开

如果TWS和API客户端之间的socket连接有问题,例如,如果TWS突然关闭,这将在从socket读取的ereader线程中触发异常。如果 API 客户端尝试使用已在使用的客户端 ID 进行连接,也会发生此异常。

socket EOF 在不同 API 语言中的处理方式略有不同。例如,在Java中,它被捕获并发送到客户端应用程序IBApi::EWrapper::error,错误代码为507:“坏消息”。在 C# 中,它被捕获并发送到IBApi::EWrapper::error,错误代码为 -1。客户端应用程序需要处理此错误消息,并使用它来指示套接字连接中已引发异常。关联的函数(如IBApi::EWrapper::connectionClosedIBApi::EClient::IsConnected函数)不会由 API 代码自动调用,但需要在 API 客户端级别*进行处理。

  • API 版本 973.04 中已更改

TWS API相关的教程

【TWS API使用教程1】—如何在自己创建的client和TWS之间创建一个连接,并请求当前的时间
【TWS API使用教程2】—如何使用 TWS API在ubuntu和windows上分别设置contract、获取contract详细信息、设置order、下单、获取持仓信息、获取账户信息
【TWS API使用教程3】—如何使用TWS API从盈透证券中设置contract及获取contract的信息?
【TWS API使用教程4】—如何使用TWS API在盈透证券中设置order?
【TWS API使用教程5】—如何使用TWS API在盈透证券中下单(place order)、获取订单信息、获取持仓、获取账户汇总信息?
【TWS API使用教程6】—如何使用TWS API在盈透证券中获取数据?
【TWS API 使用教程7】如何使用TWS API 从盈透证券中筛选满足一定条件的contract?
【TWS API 使用教程8】一个基于TWS API的简单的程序化策略


版权所有,转载本站文章请注明出处:云子量化, http://www.woniunote.com/article/72
上一篇:【TWS API 翻译系列】5、TWS API的故障排除和支持
下一篇:【TWS API 翻译系列】7、TWS API和IB中的金融工具介绍