#IB TWS编程手记——01.TWS建立基础连接

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

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

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

#IB TWS编程手记——01.TWS建立基础连接_第1张图片

如何获取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();
    }

成功的输出

#IB TWS编程手记——01.TWS建立基础连接_第2张图片

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

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

你可能感兴趣的:(自动量化交易技术)