Автономный воспроизводимый пример API-интерфейса Python IB с asyncio

Превосходный ib_insync обеспечивает высокоуровневый синхронный интерфейс к API асинхронного IB с использованием встроенного модуля asyncio. Тем не менее, существуют ситуации, когда можно было бы отказаться от использования сторонних модулей. Моя попытка состоит в том, чтобы предоставить сообществу автономный воспроизводимый пример того, как использовать нативный ibapi со встроенным Python asyncio модуль для обработки обратных вызовов. Например, допустим, что нужно добавить метод для преобразования вектора весов в фактическое количество акций, которые будут проданы. Это потребует вызова reqAccountSummary (чтобы проверить количество денег на счете) и reqMktData (чтобы получить снимок рыночных цен) в том же методе и ждать обратных вызовов, accountSummaryEnd а также tickSnapshotEnd соответственно.

Теперь следует (более простой) пример, где я вызываю reqContractDetails и ждать звонка contractDetailsEnd в том же методе runSyncContractReq, Та же самая логика теоретически может быть применена ко всем запросам с соответствующим обратным вызовом. К сожалению, это пока не совсем работает, поскольку программа "ждет" будущего обратного вызова навсегда.

Я надеюсь, что член сообщества может помочь сделать пример для правильной работы, чтобы помочь новичкам с ibapi а также asyncio как я.

from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
import asyncio


class TwsApp(EWrapper, EClient):
    def __init__(self):
        self._nextValidId = None
        EWrapper.__init__(self)
        EClient.__init__(self, wrapper=self)
        # self.future stores IDs associated with asyncio.Future() objects.
        self.future = dict()

    def get_nextValidId(self):
        """Returns a valid ID useful to send requests to TWS or place orders."""
        reqId = self._nextValidId
        self._nextValidId += 1
        return reqId

    def nextValidId(self, orderId: int):
        """This method receives valid OrderIds form TWS whenever calling
        self.run() or self.reqIds()."""
        self._nextValidId = orderId
        self.runSyncContractReq()

    async def reqContractDetails(self, reqId, contractDetails):
        print('reqContractDetails STARTED')
        super().reqContractDetails(reqId, contractDetails)
        print('reqContractDetails AWAITING for Future to be done.')
        await self.future[reqId]
        print('reqContractDetails AWAITED!')

    def contractDetails(self, reqId: int, contractDetails):
        print('contractDetails RECEIVED!')

    def contractDetailsEnd(self, reqId):
        print('contractDetailsEnd STARTED')
        self.future[reqId] = self.future[reqId].set_result('done')
        print('contractDetailsEnd FINISHED')

    def runSyncContractReq(self):
        # Prepare data.
        contract = Contract()
        contract.conId = 756733
        contract.exchange = 'SMART'
        idReq = self.get_nextValidId()

        # asyncio.
        self.future[idReq] = asyncio.Future()
        loop = asyncio.get_event_loop()
        print('Running asyncio.ensure_future')
        asyncio.ensure_future(self.reqContractDetails(idReq, contract))
        print('Running loop.run_until_complete')
        loop.run_until_complete(self.future[idReq])
        loop.close()


if __name__ == '__main__':
    app = TwsApp()
    app.connect(host='127.0.0.1', port=7497, clientId=0)
    app.run()

### OUTPUT
Running asyncio.ensure_future
Running loop.run_until_complete
reqContractDetails STARTED
reqContractDetails AWAITING for Future to be done.

# However, contract details are somehow returned once the application is stopped.
ERROR:root:ERROR -1 2104 Market data farm connection is OK:usfuture
ERROR:root:ERROR -1 2104 Market data farm connection is OK:afarm
ERROR:root:ERROR -1 2104 Market data farm connection is OK:usfarm
ERROR:root:ERROR -1 2106 HMDS data farm connection is OK:ushmds
contractDetails RECEIVED!
contractDetailsEnd STARTED
contractDetailsEnd FINISHED

0 ответов

Другие вопросы по тегам