简体   繁体   English

如何在 Python 中接收来自 IBs API 的数据?

[英]How do I receive the data coming from IBs API in Python?

Interactive Brokers just released a python version of their API.盈透证券刚刚发布了他们 API 的 Python 版本。 I am trying to get data.我正在尝试获取数据。

I am using the 'examples' in 'Program.py', and just trying to get account values.我正在使用“Program.py”中的“示例”,只是试图获取帐户值。 I just want to know what the account liquidation value is, and get that into python.我只想知道账户清算价值是多少,然后把它输入python。 This is the documentation.这是文档。 And this is the code to create and send the request:这是创建和发送请求的代码:

        app = TestApp()
        app.connect("127.0.0.1", 4001, clientId=0)
        print("serverVersion:%s connectionTime:%s" % (app.serverVersion(),
                                                   app.twsConnectionTime()))
        app.reqAccountSummary(9004, 'All', '$LEDGER')

I can use the IB Gateway, and see the request being sent, and the response coming back into IB Gateway.我可以使用 IB 网关,查看正在发送的请求,以及返回到 IB 网关的响应。 I cannot figure out how to get the response into Python.我无法弄清楚如何将响应输入 Python。 If I am reading the docs correctly, I see this:如果我正确阅读文档,我会看到:

Receiving

Summarised information is delivered via IBApi.EWrapper.accountSummary and IBApi.EWrapper.accountSummaryEnd

    1 class TestWrapper(wrapper.EWrapper):
...
    1     def accountSummary(self, reqId: int, account: str, tag: str, value: str,
    2                        currency: str):
    3         super().accountSummary(reqId, account, tag, value, currency)
    4         print("Acct Summary. ReqId:", reqId, "Acct:", account,
    5               "Tag: ", tag, "Value:", value, "Currency:", currency)
    6 
...
    1     def accountSummaryEnd(self, reqId: int):
    2         super().accountSummaryEnd(reqId)
    3         print("AccountSummaryEnd. Req Id: ", reqId)

What do I do with this?我该怎么办? It seems like I call this function to get the values, but this function is requiring as an input the value I want returned!好像我调用这个函数来获取值,但是这个函数需要我想要返回的值作为输入! What am I missing!??!我错过了什么!??!

Thanks for any help anyone can provide.感谢任何人都可以提供的任何帮助。

EDIT:编辑:

This is the 'callback' I think:这是我认为的“回调”:

@iswrapper
# ! [accountsummary]
def accountSummary(self, reqId: int, account: str, tag: str, value: str,
                   currency: str):
    super().accountSummary(reqId, account, tag, value, currency)
    print("Acct Summary. ReqId:", reqId, "Acct:", account,
          "Tag: ", tag, "Value:", value, "Currency:", currency)

And this is where I am confused.这就是我感到困惑的地方。 This seems to expect a value for the account ('value: str' in the declaration), which is exactly what I am asking it to produce.这似乎期望帐户的值(声明中的'value:str'),这正是我要求它产生的。 I cannot find where I would say somehting like the following:我找不到我会说类似以下内容的地方:

myMonies = whateverTheHellGetsTheValue(reqID)

So, 'myMonies' would then hold the account value, and I can continue on my merry way.因此,“myMonies”将保留账户价值,我可以继续我的快乐之路。

I answered a very similar question here.我在这里回答了一个非常相似的问题。 https://stackoverflow.com/a/42868938/2855515 https://stackoverflow.com/a/42868938/2855515

Here is a program where I subclass the EWrapper and EClient in the same class and use that for everything, requests and receiving callbacks.这是一个程序,我将EWrapperEClient子类在同一个类中,并将其用于所有内容、请求和接收回调。

You call EClient methods to request data and it is fed back through the EWrapper methods.您调用 EClient 方法来请求数据,并通过 EWrapper 方法反馈。 Those are the ones with the @iswrapper notation.这些是带有@iswrapper符号的那些。

from ibapi import wrapper
from ibapi.client import EClient
from ibapi.utils import iswrapper #just for decorator
from ibapi.common import *

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

    @iswrapper
    def nextValidId(self, orderId:int):
        print("setting nextValidOrderId: %d", orderId)
        self.nextValidOrderId = orderId
        # here is where you start using api
        self.reqAccountSummary(9002, "All", "$LEDGER")

    @iswrapper
    def error(self, reqId:TickerId, errorCode:int, errorString:str):
        print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)

    @iswrapper
    def accountSummary(self, reqId:int, account:str, tag:str, value:str, currency:str):
        print("Acct Summary. ReqId:" , reqId , "Acct:", account, 
            "Tag: ", tag, "Value:", value, "Currency:", currency)

    @iswrapper
    def accountSummaryEnd(self, reqId:int):
        print("AccountSummaryEnd. Req Id: ", reqId)
        # now we can disconnect
        self.disconnect()

def main():
    app = TestApp()
    app.connect("127.0.0.1", 7497, clientId=123)
    app.run()

if __name__ == "__main__":
    main()

To other newbies like me:对于像我这样的其他新手:

Note also;注意也; that i was trying to:我试图:

    print(self.reqHistoricalData(4102, ContractSamples.EurGbpFx(), queryTime,
                               "1 M", "1 day", "MIDPOINT", 1, 1, False, []))

or alternatively get a hold on the return of self.reqHistoricalData() .或者暂停self.reqHistoricalData()的返回。 As mentioned above by brian;正如上面提到的布赖恩; EClient sends requests and EWrapper receives back the information. EClient发送请求, EWrapper接收回信息。

So it seems like trying to get a handle on self.reqHistoricalData() wont get you anything (i get None type)所以似乎试图处理self.reqHistoricalData()不会让你得到任何东西(我得到None类型)

However adding the request into但是将请求添加到

    @iswrapper
    def nextValidId(self, orderId:int):
        print("setting nextValidOrderId: %d", orderId)
        self.nextValidOrderId = orderId
        # here is where you start using api
        self.reqAccountSummary(9002, "All", "$LEDGER")
        self.reqHistoricalData(4102, ContractSamples.EurGbpFx(), queryTime,
                               "1 M", "1 day", "MIDPOINT", 1, 1, False, [])

        contract = Contract()
        contract.symbol = "AAPL"
        contract.secType = "STK"
        contract.currency = "USD"
        contract.exchange = "SMART"

        self.reqHistoricalData(4103, contract, queryTime,
                               "1 M", "1 day", "MIDPOINT", 1, 1, False, [])

is enough to get the receiver ( EWrapper ) to print to console足以让接收器( EWrapper )打印到控制台

Updated 2019-01-05: The EWrapper needs to know what to do with received messages. 2019-01-05 更新: EWrapper需要知道如何处理收到的消息。 In order to allow EWrapper to provide a handle for you to eg print to console;为了让EWrapper为您提供一个句柄,例如打印到控制台; the writer of the code must specify decorator statements into the code beyond the line that says "# here is where you start using api"代码的编写者必须在代码中指定装饰器语句,超出"# here is where you start using api"的行

As an example: if the code includes this code snippet:例如:如果代码包含此代码段:

    @iswrapper
    def nextValidId(self, orderId:int):
        print("setting nextValidOrderId: %d", orderId)
        #super().nextValidId(orderId)
        self.nextValidOrderId = orderId
        #here is where you start using api

        queryTime = (datetime.datetime.today() - datetime.timedelta(days=5)).strftime("%Y%m%d %H:%M:%S")        
        self.reqHistoricalData(4102, ContractSamples.EurGbpFx(), queryTime,
                        "1 M", "1 day", "MIDPOINT", 1, 1, False, [])

    @iswrapper
    def historicalData(self, reqId:int, bar: BarData):
        print("HistoricalData. ReqId:", reqId, "BarData.", bar)

then we will get a print to console.然后我们将打印到控制台。 if the the wrapper decorator method is neglected, then there is no print to console.如果包装装饰器方法被忽略,则没有打印到控制台。 such as in this code:例如在这段代码中:

    @iswrapper
    def nextValidId(self, orderId:int):
        print("setting nextValidOrderId: %d", orderId)
        #super().nextValidId(orderId)
        self.nextValidOrderId = orderId
        #here is where you start using api

        queryTime = (datetime.datetime.today() - datetime.timedelta(days=5)).strftime("%Y%m%d %H:%M:%S")        
        self.reqHistoricalData(4102, ContractSamples.EurGbpFx(), queryTime,
                        "1 M", "1 day", "MIDPOINT", 1, 1, False, [])

    #@iswrapper
    #def historicalData(self, reqId:int, bar: BarData):
    #    print("HistoricalData. ReqId:", reqId, "BarData.", bar)

So in your case look for the correct callback.所以在你的情况下寻找正确的回调。 ex if you request an option (ie testbed/contractOperations_req ).例如,如果您请求一个选项(即testbed/contractOperations_req )。 The result goes into contractDetails (@iswrapper) where you can specify what you want to do... maybe print(contractDetails.summary.symbol) etc. So just find the respective callback for the account info and then print/return/etc.结果进入contractDetails (@iswrapper),您可以在其中指定您想要做什么......也许是print(contractDetails.summary.symbol)等。所以只需找到帐户信息的相应回调,然后打印/返回/等。 it back to your program.它回到你的程序。

This script (get-account.py) will return the value of the user's account "value" at the line of code:此脚本 (get-account.py) 将在以下代码行返回用户帐户“值”的值:

print(f"myfunds > {app.get_myfunds()}") print(f"myfunds > {app.get_myfunds()}")

... which could (of course) be assigned to a variable for further processing etc. ...可以(当然)将其分配给变量以进行进一步处理等。

What's important here is that the account "value" is available within the procedural / scripted portion of the code as a method call to the app object.这里重要的是帐户“值”在代码的过程/脚本部分中作为对应用程序对象的方法调用可用。

I put sections of the scripted portion to sleep within a while loop "time.sleep(1)" to allow time for the asynchronous "EWrapper" callback methods to return a response from IB's API (before proceeding to the next line).我将脚本部分的部分放在一个while循环“time.sleep(1)”中休眠,以便异步“EWrapper”回调方法有时间从IB的API返回响应(在继续下一行之前)。

The run_loop() is called from within a thread.从线程内调用 run_loop()。 I'm fairly new at multi-threaded programming so I don't have any real insight as to why this works but it seems to be a key to the scripts successful execution.我在多线程编程方面相当新,所以我对它的工作原理没有任何真正的见解,但它似乎是脚本成功执行的关键。 I tried running the script without multithreading and it just hung forcing me to kill the process > "kill -9 PID".我尝试在没有多线程的情况下运行脚本,它只是挂起迫使我终止进程>“kill -9 PID”。

from ibapi.client import EClient
from ibapi.wrapper import EWrapper

import threading
import time

class IBapi(EWrapper, EClient):

    def __init__(self):
        EClient.__init__(self, self)

    # Custom attributes
        self.myfunds = ''
        self.connection_status = False

    def get_myfunds(self):
        """ Custom getter method that returns current value of custom attribute self.myfunds. """
        return self.myfunds

    def get_connection_status(self):
        """ Custom getter method that returns current value of custom attribute self.connection_status. """
        return self.connection_status

    # @iswrapper
    def accountSummary(self, reqId:int, account:str, tag:str, value:str, currency:str):
        """ Returns the data from the TWS Account Window Summary tab in response to reqAccountSummary(). """
        # Update value of custom attribute self.myfunds
        self.myfunds = value

    # @iswrapper
    def accountSummaryEnd(self, reqId:int):
        """ This method is called once all account summary data for a given request are received. """
        self.done = True  # This ends the messages loop

    # @iswrapper
    def connectAck(self):
        """ Callback signifying completion of successful connection. """
        # Update value of custom attribute self.connection_status
        self.connection_status = True

def run_loop():
    app.run()

app = IBapi()

# IP: ipconfig /all > Ethernet adapter Ethernet > IPv4 Address
# Production port: 7496 | Sandbox (paper account) port: 7497
# Client ID: 123 (used to identify this script to the API, can be any unique positive integer).
app.connect('127.0.0.1', 7497, 123)

# Start the socket in a thread
api_thread = threading.Thread(target=run_loop, daemon=True)
api_thread.start()

while app.get_connection_status() == False:
    time.sleep(1) # Sleep interval to allow time for connection to server

print(f"Connection status > {app.get_connection_status()}")

app.reqAccountSummary(reqId = 2, groupName = "All", tags = "TotalCashValue")

while app.get_myfunds() == '':
    time.sleep(1) # Sleep interval to allow time for incoming data

if app.get_myfunds() != '':
    print(f"myfunds > {app.get_myfunds()}")

app.disconnect()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM