Как я могу получить данные, поступающие от IBs API в Python?

Interactive Brokers только что выпустили Python-версию своего API. Я пытаюсь получить данные.

Я использую "примеры" в "Program.py", и просто пытаюсь получить значения учетной записи. Я просто хочу узнать, что такое ликвидационная стоимость аккаунта, и передать это в python. Это документация. И это код для создания и отправки запроса:

        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)

Что мне с этим делать? Кажется, что я вызываю эту функцию, чтобы получить значения, но эта функция требует в качестве входного значения, которое я хочу вернуть! Что мне не хватает!??!

Спасибо за любую помощь, которую может оказать любой.

РЕДАКТИРОВАТЬ:

Это "обратный вызов", я думаю:

@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" будет содержать значение учетной записи, и я могу продолжать свой веселый путь.

4 ответа

Решение

Я ответил на очень похожий вопрос здесь. /questions/25669427/primer-python-ib-api-ne-ispolzuyuschij-ibpy/25669441#25669441

Вот программа, где я подкласс EWrapper а также EClient в том же классе и использовать это для всего, запросов и получения обратных вызовов.

Вы вызываете методы 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(). Как упоминалось выше Брайаном;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)

Так что в вашем случае ищите правильный обратный звонок. Например, если вы запрашиваете опцию (например, testbed / contractOperations_req). Результат переходит в contractDetails (@iswrapper), где вы можете указать, что вы хотите сделать... может быть, print(contractDetails.summary.symbol) и т. Д. Так что просто найдите соответствующий обратный вызов для информации об учетной записи, а затем напечатайте / верните / и т.д. вернемся к вашей программе.

Этот скрипт (get-account.py) вернет значение «значения» учетной записи пользователя в строке кода:

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

... который можно (конечно) присвоить переменной для дальнейшей обработки и т.д.

Здесь важно то, что «значение» учетной записи доступно в процедурной/сценарной части кода в виде вызова метода объекта приложения.

Я помещаю секции скриптовой части в спящий режим в цикле while "time.sleep(1)", чтобы дать время асинхронным методам обратного вызова "EWrapper" вернуть ответ от API IB (перед переходом к следующей строке).

run_loop() вызывается из потока. Я новичок в многопоточном программировании, поэтому у меня нет реального понимания того, почему это работает, но, похоже, это ключ к успешному выполнению сценариев. Я попытался запустить скрипт без многопоточности, и он просто завис, заставив меня убить процесс> «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()
Другие вопросы по тегам