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

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

Interactive Brokers 刚刚发布了他们 API 的 python 版本。我正在尝试获取数据。

我在 'Program.py' 中使用 'examples',只是想获取帐户值。我只想知道帐户清算价值是多少,然后将其输入 python。这是 documentation. 这是创建和发送请求的代码:

        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')

我可以使用 IB 网关,看到正在发送的请求,以及返回到 IB 网关的响应。我不知道如何将响应放入 Python。如果我正确阅读文档,我会看到:

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)

我该怎么办?好像我调用这个函数来获取值,但这个函数需要我想要返回的值作为输入!我错过了什么!??!

感谢任何人可以提供的帮助。

编辑:

这是我认为的'callback':

@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)

这就是我感到困惑的地方。这似乎需要一个帐户值(声明中的 'value: str'),这正是我要求它产生的值。我找不到我会说如下内容的地方:

myMonies = whateverTheHellGetsTheValue(reqID)

因此,'myMonies' 将持有账户价值,我可以继续我的快乐之路。

所以在你的情况下寻找正确的回调。例如,如果您请求一个选项(即 testbed/contractOperations_req)。结果进入 contractDetails (@iswrapper),您可以在其中指定要执行的操作...也许 print(contractDetails.summary.symbol)等。所以只需找到帐户信息的相应回调,然后 print/return/etc。它回到你的程序。

我在这里回答了一个非常相似的问题。

这是一个程序,我在同一个 class 中子class EWrapperEClient 并将其用于所有请求和接收回调。

调用EClient方法请求数据,通过EWrapper方法反馈。这些是带有 @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()

致其他像我这样的新手:

另请注意;我正在尝试:

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

或者获取 self.reqHistoricalData() 的 return。 正如布赖恩在上面提到的; EClient 发送请求,EWrapper 接收回信息。

所以看起来试图获取 self.reqHistoricalData() 的句柄不会给你任何东西(我得到 None 类型)

但是将请求添加到

    @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, [])

足以让接收器 (EWrapper) 打印到控制台

2019-01-05 更新: EWrapper 需要知道如何处理收到的消息。 为了让 EWrapper 为您提供句柄,例如打印到控制台;代码的编写者必须在 "# here is where you start using api"

行之外的代码中指定装饰器语句

举个例子: 如果代码包含此代码段:

    @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)

然后我们将打印到控制台。 如果包装器装饰器方法被忽略,则不会打印到控制台。 例如在这段代码中:

    @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)

此脚本 (get-account.py) 将 return 代码行处的用户帐户“值”的值:

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

...(当然)可以将其分配给变量以进行进一步处理等

这里重要的是帐户“值”在代码的过程/脚本部分中作为对应用程序对象的方法调用可用。

我将脚本部分的某些部分置于 while 循环“time.sleep(1)”内休眠,以便让异步“EWrapper”回调方法有时间 return 来自 IB 的 API(在继续下一行之前)。

run_loop() 是从一个线程中调用的。我在 multi-threaded 编程方面相当陌生,所以我对它为什么有效没有任何真正的了解,但这似乎是脚本成功执行的关键。我尝试了 运行 没有多线程的脚本,它只是挂起迫使我终止进程>“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()