盈透证券TWS API研究系列2 – TWS建立基础连接

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

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

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

TWS API 交互逻辑

如何获取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,你可以在我的博客下面留言,有空的时候我会回复你的。

美国盈透证券优惠信息(新、老盈透用户均适用):

盈透证券优惠开户链接:https://www.e-investingguide.com/interactivebrokers

无论你是已经在盈透证券入金交易的老客户或者是正准备在盈透证券开户的新客户,如果你需要免除盈透证券每月10美元账户维护费以及降低盈透证券账户交易佣金水平(具体能优惠和降低多少,取决于你的交易量,已经入金交易的盈透用户也能挂靠降低佣金),都可以联系我,在IB盈透证券出入金及TWS软件使用、API接口使用、开立机构类账户(离岸对冲基金、家族办公室、自营交易集团)等方面遇到问题的也可以联系我询问。 可以发Email邮件联系我,我的邮箱地址是 giftlord@gmail.com

我的微信号:47268101 或者打开微信,直接扫描下方二维码添加我为好友即可咨询:

第一个简单的程序

话不多说,我们直接上第一个程序。我的程序都采用了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();
}

成功的输出:

运行结果

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

在下一章中,我们将在这个代码的基础上,创建一个简易的订单。
————————————————
版权声明:本文为CSDN博主「打码的阿通」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/poisonchry/article/details/98332080

Be the first to comment on "盈透证券TWS API研究系列2 – TWS建立基础连接"

Leave a comment