2017-03-17 12 views
6

Czy ktoś może mi pomóc dowiedzieć się, jak wykonać podstawowe żądanie za pomocą gniazda IB API Python? (Używam najnowszego API IB i wygląda na to, że obsługuje on Python, więc nie powinien potrzebować Ibpy, którego używali ludzie).Próbka IB API Python nie używa Ibpy

Mój kod taki jak ten może po prostu zadziałać i sprawić, że będzie się łączyć z TWS. Problem polega na tym, że nie mam pojęcia, jak "zobaczyć" wiadomość odsyłającą z IB.

from ibapi import wrapper 
from ibapi.client import EClient 
from ibapi.contract import * 


w = wrapper.EWrapper() 
myTWS = EClient(w) 
myTWS.connect(host='localhost', port=7496, clientId=100) 

print("serverVersion:%s connectionTime:%s" % (myTWS.serverVersion(), 
              myTWS.twsConnectionTime())) 
myTWS.startApi() 


c = Contract() 
c.m_symbol = "AAPL" 
c.m_secType = "STK" 
c.m_exchange = "ISLAND" 
c.m_currency = "USD" 


myTWS.reqRealTimeBars(999, c, 5, "MIDPOINT", True, []) 

Wiem, że to było coś podobnego do Register() wcześniej z IBPy. Po prostu nie wiem, jak to zrobić w bieżącym API oryginalnego Pythona IB. Czy ktoś może pomóc, dając mi prosty przykład? Z góry dziękuję.

Odpowiedz

3

Musisz podać podklasę/przesłonić/zaimplementować wrapper.EWrapper. To tam mówisz EClient, aby wysłać dane otrzymane z TWS.

Usunąłem prawie wszystko z przykładowego programu i to działa.

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 * 
from ibapi.ticktype 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 
     contract = Contract() 
     contract.symbol = "AAPL" 
     contract.secType = "STK" 
     contract.currency = "USD" 
     contract.exchange = "SMART" 
     self.reqMktData(1101, contract, "", False, None) 

    @iswrapper 
    def error(self, reqId:TickerId, errorCode:int, errorString:str): 
     print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString) 

    @iswrapper 
    def tickPrice(self, reqId: TickerId , tickType: TickType, price: float, 
        attrib:TickAttrib): 
     print("Tick Price. Ticker Id:", reqId, "tickType:", tickType, "Price:", price) 
     #this will disconnect and end this program because loop finishes 
     self.done = True 

def main(): 
    app = TestApp() 
    app.connect("127.0.0.1", 7496, clientId=123) 
    print("serverVersion:%s connectionTime:%s" % (app.serverVersion(), 
               app.twsConnectionTime())) 
    app.run() 

if __name__ == "__main__": 
    main() 

Po wywołaniu app.run() program uruchamia się niemal nieskończoną pętlę czytanie wiadomości więc trzeba w jakiś inny sposób tworzenia struktury programu, ponieważ pętla musi być uruchomiony.

+0

dziękuję bardzo za wejście. Chętnie go wypróbuję, ale z jakiegoś powodu otrzymuję komunikat "błąd serwera" i nie mogę zalogować się do TWS w tym momencie. Wrócę, aby oznaczyć to jako odpowiedź, gdy tylko się połączę i będę mógł wypróbować twój kod. Dzięki. –

+0

Możesz testować, logując się za pomocą edemo/demouser. To jest to co zrobiłem. Zauważyłem, że używasz 7496, co oznacza konto rzeczywiste lub konto demo. Sugeruję testowanie z demo lub kontem papierowym, jeśli je masz (papier ma zwykle port 7497). – brian

+0

z kodem: self.done = True, funkcja Run breaks. i podniesiony błąd: wyjątek w wątku Thread-1 :, wiersz 113, w _recvAllMsg, buf = self.socket.recv (4096) AttributeError: Obiekt "NoneType" nie ma atrybutu "recv". Pomyślałem, że to spowodowane przez .disconnection. Czy widzisz ten sam problem? Masz pomysł, jak bezpiecznie odłączyć się i wyjść z programu? Dzięki. –

3

Szukałem sposobu na przetwarzanie sekwencji żądań poza obiektem aplikacji.

To jest moja mała modyfikacja kodu Briana (dziękując Brianowi za wstęp, jak z nim pracować), która otrzymuje dwa szczegóły kontraktu, najpierw żąda wyników kontraktu dla MSFT, potem dla IBM.

  • app.run() kończy się po otrzymaniu wszystkich wiadomości contractDetails ustawiając app.done = true w contractDetailsEnd metoda
  • gdy app.done jest ustawiona na True(), rozłącza klienta w EClient.run() metoda. Nie zorientowali się, jak rzucić metodę EClient.run() bez odłączania, więc zmieniłem wychodzenia w kodzie źródłowym w client.py EClient.run() metoda:

    finally: 
        #self.disconnect() # Myk prevent disconnect 
        return #Myk instead of disconnect return 
    
  • po tym app.run() jest zakończone bez rozłączenia i może zostać wywołane ponownie, ale musisz najpierw ustawić app.done na False, w przeciwnym razie metoda run() kończy się
  • musisz rozłączyć się na koniec bye
  • metoda disconnect() wyrzuca błąd , ale jak ktoś powiedział, wygląda na to, że możesz go zignorować, zwłaszcza jeśli odłączysz na końcu kodu

Jeśli ktoś zna lepszy sposób bez zmiany kodu źródłowego API, będę zadowolony, jeśli dasz mi radę.

Oto kod:

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 * 
from ibapi.ticktype import * 

class TestApp(wrapper.EWrapper, EClient): 
    def __init__(self): 
     wrapper.EWrapper.__init__(self) 
     EClient.__init__(self, wrapper=self) 
     self.reqIsFinished = True 
     self.started = False 
     self.nextValidOrderId = 0 

    @iswrapper 
    def nextValidId(self, orderId:int): 
     print("setting nextValidOrderId: %d", orderId) 
     self.nextValidOrderId = orderId 
     # we can start now 

    @iswrapper 
    def error(self, reqId:TickerId, errorCode:int, errorString:str): 
     print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " ,  errorString) 

    @iswrapper 
    # ! [contractdetails] 
    def contractDetails(self, reqId: int, contractDetails: ContractDetails): 
     super().contractDetails(reqId, contractDetails) 
     print("ContractDetails. ReqId:", reqId, contractDetails.summary.symbol, 
       contractDetails.summary.secType, "ConId:", contractDetails.summary.conId, 
      "@", contractDetails.summary.exchange) 
     # ! [contractdetails] 

    @iswrapper 
    # ! [contractdetailsend] 
    def contractDetailsEnd(self, reqId: int): 
     super().contractDetailsEnd(reqId) 
     print("ContractDetailsEnd. ", reqId, "\n") 
     self.done = True # This ends the messages loop 
     # ! [contractdetailsend] 

def main(): 
    app = TestApp() 
    app.connect("127.0.0.1", 4001, clientId=123) 
    print("serverVersion:%s connectionTime:%s" % (app.serverVersion(), 
              app.twsConnectionTime())) 

    print('MSFT contract details:') 
    contract = Contract() 
    contract.symbol = "MSFT" 
    contract.secType = "STK" 
    contract.currency = "USD" 
    contract.exchange = "" 
    app.reqContractDetails(210, contract) 
    app.run() 

    print('IBM contract details:') 
    contract.symbol = "IBM" 
    app.done = False # must be set before next run 
    app.reqContractDetails(210, contract) 
    app.run() 

    app.disconnect() 

if __name__ == "__main__": 
    main() 
+0

Wielkie dzięki Myk - to powinna być zaakceptowana odpowiedź. –

+0

Myk, możesz po prostu umieścić tyle żądań, ile chcesz w metodzie nextValidId(). Po prostu nie przekraczaj limitu 50 msgs/sec. – brian

+0

Cześć Myk, jakikolwiek pomysł, dlaczego nie dostałem modułu ibapi. Czy pracujesz z anakondą? Czy pracujesz z kodem Visual Studio? – pashute

2

To jest przykład jak przetwarzać wiadomości API używając wielowątkowość. App.run() jest uruchamiany jako osobny wątek i nasłuchuje odpowiedzi TWS API. Główny program wysyła następnie 5 wniosków o szczegóły dotyczące zamówienia, a następnie program główny czeka 10 sekund na odpowiedź. Wiadomości TWS API są przechowywane w instancji aplikacji i prostych sygnałach semaforów, gdy odpowiedź jest gotowa do przetworzenia.

To jest mój pierwszy program wielowątkowy, wszelkie komentarze są mile widziane.

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 * 
from ibapi.ticktype import * 
#from OrderSamples import OrderSamples 
import threading 
import time 

class myThread (threading.Thread): 
    def __init__(self, app, threadID, name): 
     threading.Thread.__init__(self) 
     self.threadID = threadID 
     self.name = name 
     self.app = app 

    def run(self): 
     print ("Starting application in separate thread:", self.name,  "threadID:", self.threadID ) 
     self.app.run() 
     print ("Exiting " + self.name) 

class TestApp(wrapper.EWrapper, EClient): 
    def __init__(self): 
     wrapper.EWrapper.__init__(self) 
     EClient.__init__(self, wrapper=self) 
     self.started = False 
     self.nextValidOrderId = 0 
     self.reqData = {}  # store data returned by requests 
     self.reqStatus = {}  # semaphore of requests - status End will indicate request is finished 


@iswrapper 
def nextValidId(self, orderId:int): 
    print("setting nextValidOrderId: %d", orderId) 
    self.nextValidOrderId = orderId 


@iswrapper 
def error(self, reqId:TickerId, errorCode:int, errorString:str): 
    print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString) 

@iswrapper 
# ! [contractdetails] 
def contractDetails(self, reqId: int, contractDetails: ContractDetails): 
    super().contractDetails(reqId, contractDetails) 
    # store response in reqData dict, for each request several objects are appended into list 
    if not reqId in self.reqData: 
     self.reqData[reqId] = [] 
    self.reqData[reqId].append(contractDetails) # put returned data into data storage dict 
    # ! [contractdetails] 

@iswrapper 
# ! [contractdetailsend] 
def contractDetailsEnd(self, reqId: int): 
    super().contractDetailsEnd(reqId) 
    print("ContractDetailsEnd. ", reqId, "\n") # just info 
    self.reqStatus[reqId] = 'End'    # indicates the response is ready for further processing 
    # ! [contractdetailsend] 



def main(): 

    app = TestApp() 
    app.connect("127.0.0.1", 4001, clientId=123) 
    print("serverVersion:%s connectionTime:%s" % (app.serverVersion(), 
              app.twsConnectionTime())) 

    thread1App = myThread(app, 1, "Thread-1") # define thread for sunning app 
    thread1App.start()       # start app.run(] as infitnite loop in separate thread 

    print('Requesting MSFT contract details:') 
    contract = Contract() 
    contract.symbol = "MSFT" 
    contract.secType = "STK" 
    contract.currency = "USD" 
    contract.exchange = "" 
    app.reqStatus[210] = 'Sent' # set request status to "sent to TWS" 
    app.reqContractDetails(210, contract) 

    print('Requesting IBM contract details:') 
    contract.symbol = "IBM" 
    app.reqStatus[211] = 'Sent'   
    app.reqContractDetails(211, contract) 

    print('Requesting IBM contract details:') 
    contract.symbol = "GE" 
    app.reqStatus[212] = 'Sent' 
    app.reqContractDetails(212, contract) 

    print('Requesting IBM contract details:') 
    contract.symbol = "GM" 
    app.reqStatus[213] = 'Sent' 
    app.reqContractDetails(213, contract) 

    print('Requesting IBM contract details:') 
    contract.symbol = "BAC" 
    app.reqStatus[214] = 'Sent' 
    app.reqContractDetails(214, contract) 

    i = 0 
    while i < 100:   # exit loop after 10 sec (100 x time.sleep(0.1) 
     i = i+1 
     for reqId in app.reqStatus: 
      if app.reqStatus[reqId] == 'End': 
       for contractDetails in app.reqData[reqId]: 
        print("ContractDetails. ReqId:", reqId, contractDetails.summary.symbol, 
        contractDetails.summary.secType, "ConId:", contractDetails.summary.conId, 
        "@", contractDetails.summary.exchange) 
       app.reqStatus[reqId] = 'Processed' 
     time.sleep(0.1) 
    app.done = True    # this stops app.run() loop 

if __name__ == "__main__": 
    main() 
+0

Hej, chcę to zobaczyć w akcji, ale mam ten błąd: "ImportError: Brak modułu o nazwie" OrderSamples " – Jeremy

+0

OrderSamples jest w folderze demonstracyjnym, ale nie jest to konieczne w tym przykładzie, więc skomentowałem to. – Myk

1

Istnieje nowy projekt, który upraszcza pracę z Python TWS Api.

Nazywa się Insync IB i umożliwia zarówno przetwarzanie synchroniczne, jak i asynchroniczne. Wygląda bardzo dobrze dla początkujących w TWS API. Link to Project Page

Przykład wnioskowania danych historycznych za pomocą IB-InSync:

from ib_insync import * 

ib = IB() 
ib.connect('127.0.0.1', 7497, clientId=1) 

contract = Forex('EURUSD') 
bars = ib.reqHistoricalData(contract, endDateTime='', durationStr='30 D', 
    barSizeSetting='1 hour', whatToShow='MIDPOINT', useRTH=True) 

# convert to pandas dataframe: 
df = util.df(bars) 
print(df[['date', 'open', 'high', 'low', 'close']])