Пример Python IB API, не использующий Ibpy
Может ли кто-нибудь помочь мне понять, как выполнить базовый запрос с помощью сокета IB API Python? (Я использую последний API IB, и кажется, что он поддерживает 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, [])
Я знаю, что это было что-то вроде Register() раньше с IBPy. Я просто не знаю, как это сделать в этом текущем исходном API Python для IB. Может ли кто-нибудь помочь, дав мне простой пример? Заранее спасибо.
4 ответа
Вы должны создать подкласс / переопределить / реализовать 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()
программа запускает почти бесконечный цикл чтения сообщений, поэтому вам понадобится другой способ структурирования вашей программы, поскольку цикл должен быть запущен.
Появился новый проект, который упрощает работу с Python TWS Api.
Он называется IB-insync и позволяет выполнять синхронизацию и асинхронную обработку. Это выглядит очень хорошо для новичков в TWS API. Ссылка на страницу проекта
Пример запроса исторических данных с использованием 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']])
Я искал способ обработки последовательности запросов вне объекта приложения.
Это моя маленькая модификация кода Брайана (спасибо Брайану за представление о том, как с ним работать), который получает две детали контракта, сначала он запрашивает детали контракта для MSFT, а затем для IBM.
- app.run() завершается после получения всех сообщений contractDetails путем установки app.done = True в методе contractDetailsEnd()
когда app.done имеет значение True, клиент отключается в методе EClient.run(). Я не понял, как выйти из метода EClient.run() без отключения, поэтому я изменил выход в исходном коде в методе client.py EClient.run():
finally: #self.disconnect() # Myk prevent disconnect return #Myk instead of disconnect return
- после этого app.run() завершается без отключения и может быть вызван снова, но сначала вы должны установить для app.done значение False, в противном случае метод run () завершается
- ты должен отключиться в конце пока сам
- Метод connect() выдает ошибку, но, как кто-то сказал, кажется, вы можете игнорировать ее, особенно если вы отключаетесь в конце кода
Если кто-то знает лучший способ без изменения исходного кода 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()
Это пример того, как обрабатывать сообщения API с использованием многопоточности. App.run() запускается как отдельный поток и прослушивает ответы TWS API. Затем основная программа отправляет 5 запросов на ContractDetails, а затем основная программа ожидает ответа в течение 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()