盈透证券TWS API研究系列3 – 提交你的第一个Hello World!

关于IB盈透的错误码:

在我们进一步深入IB盈透的工作机理前,需要回到Wrapper中error函数,如果善用error函数提供的错误信息,将能帮助我们快速定位到错误,并且修正我们的代码。

关于IB错误Code,具体的编码,请查考网站 https://interactivebrokers.github.io/tws-api/message_codes.html

这里,我重点说一下error函数中,带有id函数,这是我们继续开发客户端算法非常重要的基础。

public void error(int id, int errorCode, string errorMsg)
{
    Console.WriteLine("TWS Error: id:{0} code:{1} message:{2}", id, errorCode, errorMsg);
}

它包含3个返回参数,整数型的id,整数型的errorCode,以及string型的errorMsg。这里的id是我们提交到TWS的任务id,也就是OrderId,当它为-1时,这个函数得到的信息,会有点类似一些Debug里面的Info级别消息,也就是告诉你当前TWS的工作状态,你的客户端与TWS连接情况,TWS与IB自身,交易所的连接情况等基础信息。

所以,上一篇文章里,我们啥也没干,在Console里一下子打出了一堆信息,就是这样的原因。

运行结果

而errorCode,就是比较有用的错误信息了,它是不同的预定义的编码,具体编码信息,你可以从上面的连接里对照着找,比如这个截图里出现的2104,在IB自己的官网定义就是:

至于message,就是返回的消息咯。

另一个带Exception的error,指的是你调用IB API时产生的错误。比如你试图发送一个订单,但是你并未建立起跟TWS的连接,这个时候就会出现TCP连接错误的问题,而这个错误,就会通过

public void error(Exception e)
{
    Console.WriteLine("API Error: thrown exception: {0}", e);
}

这个回调函数告诉你。因此,你可以使用调用栈信息,找出错误的代码位置,或者直接打印异常信息本身。

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

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

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

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

提交命令

在IB API的架构设计,所有向IB Server提交的命令,都是通过EClient封装好的API来提交。啥玩意是EClient?

EclientSocket继承关系

EclientSocket继承了Eclient和EClientMsgSink,这个类自身所担负的主要工作就是创建和维护与TWS通信的TCP信道。而任务命名请求,则全部交由EClient负责。

因此,对于TWS来说,你提交任务的基本方式,大体上是这样的:

clientSocket.reqXXXXX(id, ...);

因此,你能做什么,不能做什么,基本上就看Eclient是否封装了对应的API,以及API里需要的参数是什么样子的,所以具体的你可以看看API手册上是怎么写的。

那么明白了这一点,我们可以进入到下一个内容,向TWS提交合约信息。

合约

所谓合约,如果从金融专业出发,指的是合同,或者更侠义的说是商品交割合同,也就是期货、股指等商品的交易合同。

IB把绝大部分的交易请求行为定义为合约(Contract),这意味着用户要提交具体的数据请求时,需要像填表一样,把相关任务的信息填入到Contract中,因此,不同类型的任务,需要填写的关键字是不一样的。

IB将很多操作,比如查股价,查期货合约等任务,都视为一个合约任务,不知道这种定义更偏向金融系统的人还是更偏向IT系统的,如果用我们IT的行话说,那一个Contract其实就是一个Request。

你要是还不明白这个是什么意思,那么容许我引入一下网络开发常提到的HTTP协议。在HTTP中,我们通过GET/POST命名,并附带各种参数,以此告知远程服务器对应的任务形式。

Contract也是类似的,只不过是用类进行表示的。所以要做什么事,能做什么事,两件函数相同但功能不同的任务如何区分,思路也基本一样,就是依靠提交的Contract类的不同参数定义来区分。

这其实也可以理解,因为IB涵盖的交易产品太多太广泛,必然会出现很多重复的地方,所以一个交易产品,需要多个不同的字段去缩小它的定义。

举例来说,当你告知IB,帮我查找“苹果”。如果不告知它具体的其他信息,它可能会以为你要查找苹果现货合约,苹果股票期权合约,或者苹果股票合约;它会完全confused。

所以要怎么去查找具体的商品合约信息呢?

IB给出的建议是让我们直接在TWS中找具体的信息(通过双击,或者在Contract Info -> Description):

ContractInfo

然后我们可以得到这样的信息:

Details

这个框里的东西,告知了我们对应的合约信息,包含交易所代码,合约代码,货币单位等重要的信息。所以如果你需要TWS告诉你苹果公司的股票合约信息,而不是期权合约信息,甚至不是苹果现货合约信息,那么你需要通过这个方法,找到苹果股票的合约信息。

接下来怎么做?

还记得上一篇文章《#IB TWS编程手记——01.TWS建立基础连接》中怎样建立与TWS的连接么?

我们接下来要做的,就是在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) { }


    // 新增加的指令
    clientSocket.reqMatchingSymbols(wrapper.NextOrderId, "IB");

    // 然后啥事也不干,就睡10s
    Thread.Sleep(10000);

    // 关闭连接
    Console.WriteLine("Disconnecting from TWS...");
    clientSocket.eDisconnect();

    // 类似于断点,我们可以查看到代码的输出
    Console.ReadKey();
}

我们注意下这个新增的代码:

clientSocket.reqMatchingSymbols(wrapper.NextOrderId, "IB");

这个指令的意思,是让TWS帮我们查找合约中,合约代码为“IB”的交易产品信息。还记得我们的Wrapper中,只实现了屈指可数的几个函数吗?

现在回到Wrapper的实现类,我们需要把回调函数“symbolSamples”实现它的方法:

public void symbolSamples(int reqId, ContractDescription[] contractDescriptions)
{
    string derivSecTypes;
    Console.WriteLine("Symbol Samples. Request Id: {0}", reqId);
    foreach (var contractDescription in contractDescriptions)
    {
        derivSecTypes = "";
        foreach (var derivSecType in contractDescription.DerivativeSecTypes)
        {
            derivSecTypes += derivSecType;
            derivSecTypes += " ";
        }
        Console.WriteLine("Contract: conId - {0}, symbol - {1}, secType - {2}, primExchange - {3}, currency - {4}, derivativeSecTypes - {5}",
            contractDescription.Contract.ConId, contractDescription.Contract.Symbol, contractDescription.Contract.SecType,
            contractDescription.Contract.PrimaryExch, contractDescription.Contract.Currency, derivSecTypes);
    }
}

然后你再重新执行一遍代码,看看有什么效果?然后再去跟TWS的信息做一下对比?
————————————————
版权声明:本文为CSDN博主「打码的阿通」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/poisonchry/article/details/98731474