2017-04-26 5 views
1

Ich versuche, die neue Python Interactive Broker API, aber ich bin einige ernsthafte Geschwindigkeitsprobleme beim ersten Schritt zu erleben ...Python Interactive Brokers IB API sehr, sehr langsam

Der folgende Code (siehe unten) mal

0:00:08.832813, bis die Daten werden

0:00:36.000785, bis die App vollständig getrennt ist ... ist es Warum

erhalten getan so langsam? Was wäre der beste Weg, um es zu beschleunigen?

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

Ich habe auch versucht, um Anfragen on the fly einreichen nur mit

def runMe(): 
    app.run() # where run() has be removed from the class definition 

import threading 
thread = threading.Thread(target = runMe) 
thread.start() 

Aber es war auch unglaublich langsam, es ist der Hintergrund ständig laufen. Irgendwelche Vorschläge geschätzt

Antwort

-1

app.run() ist eine Endlosschleife während app.done == False, aber es nicht sofort zu stoppen, wenn app.done auf True gesetzt ist. (Ich weiß nicht warum).

Was ich getan habe, ist eine neue Methode statt app.run() zu schreiben.

Hier ist meine Lösung:

import time 
from ibapi import (decoder, reader, comm) 

und setzen Sie diese Funktion in Ihrem Client-Klasse.

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) 

die Verwendung ist einfach. Verwenden Sie einfach app.getMessage() anstelle von app.run()

2

Ich würde empfehlen, die Verbindung Socket-Sperre innerhalb der Verbindungsklasse im Ibapi-Modul zu ändern. Die Empfehlung kam von heshiming auf github; Wenn Sie Zugang zu den privaten interaktiven Broker Repo haben Sie Zugriff auf die Diskussion hier

Ich tat dies und es verbesserte die Leistung erheblich.

Heshiming empfiehlt, das Zeitlimit für das Socket-Lock-Objekt zu reduzieren, das jedes Mal aufgerufen wird, wenn Sie eine Nachricht senden oder empfangen. Um die Socket-Sperre zu ändern, gehen Sie zum Ordner site-packages für ibapi und ändern Sie die connect-Funktion in connection.py, indem Sie "self.socket.settimeout (1)" in "self.socket.settimeout (0.01)" ändern. Dies ist Zeile 48 in connection.py für die Version, die ich habe.

Falls Sie Heshimings Post nicht sehen können, habe ich ihn am Ende dieses Posts eingefügt.

Alternative Option: Eine weitere interessante Lösung wäre die Verwendung von asyncio für eine asynchrone Ereignisschleife. Ich habe das nicht getan, aber es sieht vielversprechend aus. Siehe das Beispiel Ewald zusammen https://github.com/erdewit/tws_async

Heshiming Kommentar:

Die Umsetzung der Verbindung /ibapi/connection.py verfügt über ein Sperrobjekt geteilt sowohl in sendMsg und recvmsg.Da connect, self.socket.settimeout (1) aufgerufen wird, daher die zugrunde liegende self.socket.recv (4096) nur einmal pro Sekunde aus.

Eine solche Implementierung verursacht ein Leistungsproblem. Da die Sperre freigegeben ist, kann der Socket beim Empfangen keine Daten senden. In dem Szenario , wo die empfangene Nachricht weniger als 4k Bytes lang ist, wartet die recvMsg Funktion für 1 Sekunde vor dem Aufheben der Sperre, wodurch sendMsg darauf warten. In meinem Experiment scheinen die meisten Nachrichten kürzer als 4 KB zu sein. Mit anderen Worten, dies legt eine Obergrenze von 1 recvMsg pro Sekunde fest.

Es gibt paar Strategien, um dies zu mildern. Man kann den Empfangspuffer auf eine Zahl viel kleiner als 4k reduzieren, oder den Socket Timeout auf etwas wie 0,001 Sekunden reduzieren, um es weniger zu blockieren.

Oder nach http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid ist die Buchse selbst tatsächlich Thread-sicher. Somit sind keine Sperren notwendig.

Ich habe alle drei Strategien ausprobiert. Das Entfernen des Schlosses funktioniert am besten. Und Reduzierung der Timeout auf 0,001 funktioniert in ähnlicher Weise.

Ich kann nur für Linux/Unix-Plattformen bürgen, und ich habe es nicht auf Windows versucht. Würden Sie erwägen, die Implementierung zu ändern, um dies zu verbessern?