我正在尝试新的Python Interactive Broker API,但我在第一步遇到了一些严重的速度问题......
以下代码(见下文)时间
0:00:08.832813
直到收到数据
0:00:36.000785
直到应用完全断开连接...
为什么这么慢? 什么是加快它的最好方法?
from ibapi import wrapper
from ibapi.client import EClient
from ibapi.utils import iswrapper #just for decorator
from ibapi.common import *
from ibapi.contract import *
import datetime
from datetime import timedelta
class DataApp(wrapper.EWrapper, EClient):
def __init__(self):
wrapper.EWrapper.__init__(self)
EClient.__init__(self, wrapper=self)
@iswrapper
def historicalData(self, reqId: TickerId, date: str, open: float, high: float,
low: float, close: float, volume: int, barCount: int,
WAP: float, hasGaps: int):
super().historicalData(reqId, date, open, high, low, close, volume,
barCount, WAP, hasGaps)
print("HistoricalData. ", reqId, " Date:", date, "Open:", open,
"High:", high, "Low:", low, "Close:", close, "Volume:", volume)
@iswrapper
def historicalDataEnd(self, reqId: int, start: str, end: str):
super().historicalDataEnd(reqId, start, end)
print("HistoricalDataEnd ", reqId, "from", start, "to", end)
print(datetime.datetime.now()-startime)
self.done = True # This ends the messages loop - this was not in the example code...
def get_data(self):
self.connect("127.0.0.1", 4002, clientId=10)
print("serverVersion:%s connectionTime:%s" % (self.serverVersion(),
self.twsConnectionTime()))
cont = Contract()
cont.symbol = "ES"
cont.secType = "FUT"
cont.currency = "USD"
cont.exchange = "GLOBEX"
cont.lastTradeDateOrContractMonth = "201706"
self.reqHistoricalData(1, cont, datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"),
"1800 S", "30 mins", "TRADES", 0, 1, [])
self.run()
self.disconnect()
print(datetime.datetime.now()-startime)
global starttime
startime = datetime.datetime.now()
DA = DataApp()
DA.get_data()
我还尝试不断运行它是后台,以便只使用
动态提交请求def runMe():
app.run() # where run() has be removed from the class definition
import threading
thread = threading.Thread(target = runMe)
thread.start()
但它也非常缓慢。 任何建议赞赏
答案 0 :(得分:2)
我建议你修改ibapi模块中连接类中的连接套接字锁。该建议来自heshiming on github;如果您有权访问私人互动经纪人回购,您可以访问https://github.com/InteractiveBrokers/tws-api/issues/464
中的讨论我这样做了,它显着提高了性能。
Heshiming建议您减少套接字锁定对象的超时,每次发送或接收消息时都会调用该超时。 要修改套接字锁定,请转到ibapi的site-packages文件夹并修改connection.py中的connect函数,将“self.socket.settimeout(1)”更改为“self.socket.settimeout(0.01)”。对于我的版本,这是connection.py中的第48行。
如果你看不到heshiming的帖子,我把它包含在这篇文章的底部。
备选方案:另一个有趣的解决方案是利用异步事件循环的asyncio。我没有这样做,但看起来很有希望。请参阅示例Ewald汇总https://github.com/erdewit/tws_async
Heshiming评论:
Connection /ibapi/connection.py的实现 在sendMsg和recvMsg中共享一个Lock对象。自连接以来 调用self.socket.settimeout(1),因此是底层的 self.socket.recv(4096)每秒只运行一次。
此类实现会产生性能问题。因为锁是 共享,套接字在接收时无法发送数据。在场景中 收到的消息长度小于4k字节,recvMsg 功能将在释放锁定之前等待1秒钟 后续sendMsg等待。在我的实验中,大多数消息似乎都是 短于4k字节。换句话说,这就是一个上限 每秒recvMsg。
有几种策略可以缓解这个问题。一个可以减少 接收缓冲区到一个小于4k的数字,或减少套接字 超时至0.001秒以使其阻塞更少。
或根据 http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid ,套接字本身实际上是线程安全的。因此没有锁 必要的。
我尝试了所有三种策略。卸下锁是最好的。和 将超时减少到0.001会以类似的方式工作。
我只能担保linux / unix平台,我还没试过 视窗。您是否考虑更改实施以改进 此?
答案 1 :(得分:0)
如@BenM在他的回答中所建议的,当前的API(2019年5月)已通过删除recvMsg
中的锁来提高速度。但是,根据请求的类型,它仍然可能很慢。
删除大多数日志记录很有帮助,大概是因为某些msg类型很大。最初,我尝试使用logging
库对其进行过滤,但从整体上来说,删除代码的速度更快,并且将其归结为生成更大字符串之前甚至需要传递给{{ 1}}。
用双端队列替换队列:用collections.deque替换logging
中的Queue
也可能是值得的,因为client.py
快10倍以上。我已经在其他地方进行了此操作以提高速度,但目前为止还没有完成。 deque
应该是线程安全的,并且不使用在此处使用的锁定方式:“双端队列支持双线程,从双端队列的任一侧进行线程安全的内存高效添加和弹出操作”(来自文档)。
答案 2 :(得分:-1)
app.run()
是app.done == False
时的无限循环,但在app.done
设置为True时不会立即停止。 (我不知道为什么)。
我所做的是编写一种新方法,而不是使用app.run()
。
这是我的解决方案:
import time
from ibapi import (decoder, reader, comm)
并将此功能放入您的客户端类。
def getMessage(self, wait=3):
# wait 3 secs for response to come in
time.sleep(wait)
# get everything in app.msg_queue
while not self.msg_queue.empty():
text = self.msg_queue.get(block=True, timeout=0.2)
fields = comm.read_fields(text)
self.decoder.interpret(fields)
用法很简单。只需使用app.getMessage()
代替app.run()