IB看起来很复杂,我该如何入手?

    科技2025-03-17  18

    IB看起来很复杂,我该如何入手?

    虽然Class List很多,但是和开发者主要打交道的就三个类,EWrapper,EClientSocket,EReaderSignal。IB的代码交互方式,是一种类似于请求/映射的结构,其背后采用了一种异步方式,用户发送了请求消息至TWS或者IB Gate,然后经过处理后提交给IB的服务器或者直接提交到交易所进行处理,然后等待从IB服务器或者交易所返回信息,再推送至客户端,客户端则通过回调函数的形式,交给对应的代码进行数据处理。

    这也是为什么IB的开发文档里,反复提及的延迟问题,也是为什么IB反复强调用户提交的每一个指令,应该有一个独一无二的标识符。因为这种结构决定了返回的消息不会按顺序返回,用户则需要通过ID辨识获得的数据是什么时候提交的,提交的是什么内容。

    如何获取IB API

    首先你需要TWS客户端:地址:https://www.interactivebrokers.com.hk/en/index.php?f=16042

    而API则在这个地址:https://www.interactivebrokers.com.hk/en/index.php?f=1325

    API References: http://interactivebrokers.github.io/tws-api/index.html

    如果还有什么不懂,你应该看看我的上一个教程TWS开发准备工作。其他的Q&A,你可以在我的博客下面留言,有空的时候我会回复你的。

    第一个简单的程序

    话不多说,我们直接上第一个程序。我的程序都采用了C#,而Java/CPP/Python等程序的编写原则上是一样的。所以你可以很容易重构自己的代码,也可以参考IB给的示例编写。目前你需要与TWS交互的一共4个类,分别是:EWrapper, EClientSocket, EReaderSignal, EReader。

    EWrapper,在前面已经说了,主要是用来处理各种消息的回调函数集合,所有像TWS/IB Gateway发送的消息请求,全部会使用EWrapper进行处理,所以开发属于自己的分析程序,其中一个重点就是处理EWrapper获得的各类回调数据。

    EClientSocket,封装的是TCP通信协议,与TWS或IB Gateway所有的通信协议都已经被封装在了EClientSocket里。IB默认使用这个类来进行数据通信,但是如果你需要使用EClientSocketSSL,进行加密通信。

    EReaderSignal,是用来获取从TWS返回的消息的,而EReader,则是用来处理消息队列的,根据IB文档描述,用于接受来自TWS的消息,并且处理相关信号的工作应该使用一个单独的后台线程进行处理。

    所以,我们首先对EWrapper的抽象方法进行实例化:

    class IBWrapper : EWrapper { ... }

    根据提示,我们会被要求实例化一大波函数,但是一开始我们需要关心,并且需要实现的只有这么几个:

    /// 这个函数负责当客户端与TWS建立连接时使用,作用类似于消息握手 public void connectAck() { if (clientSocket.AsyncEConnect) clientSocket.startApi(); } /// 这个函数负责当客户端与TWS关闭连接时使用,现在我们只打印closed消息即可 public void connectionClosed() { Console.WriteLine("Connection to TWS closed"); } /// IB API一共需要用户实现三种不同的错误信息,分别来自.NET自身的错误,来自IB API自身的异常抛出,以及来自TWS反馈的消息 /// 这个error属于IB自身异常抛出的回调处理函数 public void error(Exception e) { Console.WriteLine("API Error: thrown exception: {0}", e); } /// 这个error属于.net自身的错误,比如访问越界什么的 public void error(string str) { Console.WriteLine("Error: message: {0}", str); } /// 这个error则是TWS反馈消息的API,errorCode为-1的时候不代表着错误,而其他>0的 public void error(int id, int errorCode, string errorMsg) { Console.WriteLine("TWS Error: id:{0} code:{1} message:{2}", id, errorCode, errorMsg); } /// 这个API是用来接受返回的账户信息的,在建立连接的第一步时会调用这个函数 public void managedAccounts(string accountsList) { Console.WriteLine("Account list: " + accountsList); } /// nextValidId,根据IB描述的意思,在每一次任务下达后,都会生成一个可用的新的任务Id,因为IB的消息机制是异步的,每一个任务都用唯一的ID进行标识,并且处理,比如说你需要对不同订单下达“买入”、“卖出”命令,但是这两个命令结果返回的时间先后可能不一致,为了避免混淆,应该给这些不同的命令不同的ID public void nextValidId(int orderId) { Console.WriteLine("Next Valid Id: " + orderId); nextOrderId = orderId; }

    然后你需要准备三个变量,分别记录新的可用ID,和TWS连接的Socket,以及用于消息处理的EReaderSignal。

    /// 可用的订单ID private int nextOrderId; /// TWS连接Socket,非SSL版 private EClientSocket clientSocket; /// 消息处理类,这个不需要做太多干预 private EReaderSignal signal; /// 以及对外的访问接口 public int NextOrderId { get { return nextOrderId; } set { nextOrderId = value; } } /// 以及对外的访问接口 public EClientSocket ClientSocket { get { return clientSocket; } set { clientSocket = value; } } /// 以及对外的访问接口 public EReaderSignal Signal { get { return signal; } }

    当你完成这些工作后,再创造一个构造函数,用于初始化EWrapper。

    public IBWrapper() { signal = new EReaderMonitorSignal(); clientSocket = new EClientSocket(this, signal); }

    我们需要准备的前期工作基本完毕,现在回到Main函数

    static void Main() { /// 创建Wrapper,并且获取创建好的clientSocket以及readerSignal IBWrapper wrapper = new IBWrapper(); EClientSocket clientSocket = wrapper.ClientSocket; EReaderSignal readerSignal = wrapper.Signal; // 连接至服务器,IP地址,端口号,本客户端ID,默认为0 // 关于0有什么用,我们在后面的文章里再详细说明 clientSocket.eConnect("127.0.0.1", 7497, 0); // 创建一个reader,用于处理消息事件 var reader = new EReader(clientSocket, readerSignal); reader.Start(); // 创建后台线程,监控来自TWS的消息 new Thread(() => { while (clientSocket.IsConnected()) { readerSignal.waitForSignal(); reader.processMsgs(); } }) { IsBackground = true }.Start(); // 这一步会一直循环,直到我们获得了可用的OrderID,>0 时表示着此时开始,可以向TWS发送命名了。 while (wrapper.NextOrderId <= 0) { } // 啥事也不干,就睡10s Thread.Sleep(10000); // 与TWS关闭连接 Console.WriteLine("Disconnecting from TWS..."); clientSocket.eDisconnect(); // 类似于断点,我们可以查看到代码的输出 Console.ReadKey(); }

    成功的输出

    成功以后得到的输出就是这样的,这些信息表示我们获取的信息和数据都是正常的,连接到交易所的通信也是正常的。

    在下一章中,我们将在这个代码的基础上,创建一个简易的订单。

    Processed: 0.011, SQL: 8