出色的ib_insync使用内置的 asyncio 模块为异步 IB API 提供了更高级别的同步接口。但是,在某些情况下,人们可能更愿意避免依赖第 3 方模块。我的尝试是向社区提供一个自包含的可重现示例,说明如何使用原生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