2017-03-17 249 views
6

有人可以幫助我弄清楚如何使用IB API Python套接字來完成基本請求嗎? (我使用最新的IB API,它似乎支持Python,所以不應該需要人們曾經使用的Ibpy)IB API Python示例不使用Ibpy

我這樣的代碼可以簡單地工作,並使其連接到TWS。 問題是:我不知道如何「看到」從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, []) 

我知道它和IBPy之前的Register()類似。我只是不知道如何在當前的IB原始python API中執行此操作。有人可以通過給我一個簡單的例子來幫助嗎?提前致謝。

回答

3

你必須繼承/覆蓋/實現wrapper.EWrapper。這就是你告訴EClient發送從TWS收到的數據的地方。

我從示例程序中刪除了幾乎所有內容,並運行該示例程序。

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

一旦你調用app.run()程序啓動一個幾乎無限循環讀取郵件,所以你需要一些其他的方式來構建程序,因爲循環必須啓動。

+0

非常感謝你爲你的輸入。我急於嘗試,但由於某種原因,我收到「服務器錯誤」信息,此時無法登錄TWS。我會回來標記這個答案,一旦我連接並能夠嘗試你的代碼。謝謝。 –

+0

您可以使用edemo/demouser登錄進行測試。這就是我所做的。我注意到您使用的是7496,這意味着一個真實賬戶或模擬賬戶。如果您有一個(紙張通常是端口7497),我建議您使用演示或紙質帳戶進行測試。 – brian

+0

與代碼:self.done = True,函數運行中斷。並引發錯誤:線程Thread-1中的異常:第113行,在_recvAllMsg中,buf = self.socket.recv(4096)AttributeError:'NoneType'對象沒有屬性'recv'。我認爲這是由.disconnection引起的。你看到同樣的問題嗎?任何想法如何我可以安全斷開並退出程序?謝謝。 –

3

我已經尋找如何處理應用程序對象之外的請求序列的方式。

這是我對Brian的代碼的小小修改(感謝Brian介紹如何使用它),它獲取兩個合同詳細信息,首先請求用於MSFT的合同詳細信息,然後用於IBM。

  • app.run()是通過設置app.done =真在contractDetailsEnd接收所有contractDetails消息()方法
  • 當app.done設置爲True後完成後,在EClient.run客戶端斷開連接() 方法。我沒有想出如何退出EClient.run()方法,無需斷開,所以我改變EClient.run()方法退出在client.py的源代碼:

    finally: 
        #self.disconnect() # Myk prevent disconnect 
        return #Myk instead of disconnect return 
    
  • 是app.run後()完成,而不斷開,可以再次調用,但你必須設置app.done爲False第一,否則run()方法退出
  • ,你必須在最後告別自己斷開
  • disconnect()方法拋出錯誤,但正如有人說似乎你可以忽略它,尤其是如果你在代碼結束時斷開連接

如果有人在不更改API源代碼的情況下知道更好的方法,如果您給我一個建議,我會很高興。

下面是代碼:

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

非常感謝Myk - 這應該是被接受的答案。 –

+0

Myk,您可以在nextValidId()方法中儘可能多地添加請求。只是不要超過50個信號/秒的限制。 – brian

+0

嗨Myk,任何想法,爲什麼我找不到ibapi模塊。你在使用水蟒嗎?你在使用Visual Studio Code嗎? – pashute

2

這是示例瞭如何使用多線程處理API消息。 app.run()作爲單獨的線程啓動,它正在偵聽TWS API響應。然後主程序向ContractDetails發送5個請求,然後主程序等待響應10秒。當響應準備好處理時,TWS API消息存儲在應用實例和簡單的信號量信號中。

這是我的第一個多線程程序,歡迎任何評論。

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

嘿,我想看到在行動中,但我得到了這個錯誤:「ImportError:沒有名爲'OrderSamples'的模塊 – Jeremy

+0

OrderSamples在演示文件夾中,但在這個例子中它不是必需的,所以我已經評論了它。 – Myk

1

有一個新項目簡化了Python TWS Api的工作。

它被稱爲IB-insync,它允許同步和異步處理。 TWS API中的新手看起來非常棒。 Link to Project Page

請求使用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']])