For optimum results this notebook should be run during the Forex trading session.
from ib_insync import *
util.startLoop()
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=15)
<IB connected to 127.0.0.1:7497 clientId=15>
Create some Forex contracts:
contracts = [Forex(pair) for pair in ('EURUSD', 'USDJPY', 'GBPUSD', 'USDCHF', 'USDCAD', 'AUDUSD')]
ib.qualifyContracts(*contracts)
eurusd = contracts[0]
Request streaming ticks for them:
for contract in contracts:
ib.reqMktData(contract, '', False, False)
Wait a few seconds for the tickers to get filled.
ticker = ib.ticker(eurusd)
ib.sleep(2)
ticker
Ticker(contract=Forex('EURUSD', conId=12087792, exchange='IDEALPRO', localSymbol='EUR.USD', tradingClass='EUR.USD'), time=datetime.datetime(2019, 12, 31, 17, 5, 2, 127038, tzinfo=datetime.timezone.utc), bid=1.12245, bidSize=10700000, ask=1.1225, askSize=2000000, high=1.1239, low=1.11985, close=1.12, halted=0.0)
The price of Forex ticks is always nan. To get a midpoint price use midpoint()
or marketPrice()
.
The tickers are kept live updated, try this a few times to see if the price changes:
ticker.marketPrice()
1.1224750000000001
The following cell will start a 30 second loop that prints a live updated ticker table. It is updated on every ticker change.
from IPython.display import display, clear_output
import pandas as pd
df = pd.DataFrame(
index=[c.pair() for c in contracts],
columns=['bidSize', 'bid', 'ask', 'askSize', 'high', 'low', 'close'])
def onPendingTickers(tickers):
for t in tickers:
df.loc[t.contract.pair()] = (
t.bidSize, t.bid, t.ask, t.askSize, t.high, t.low, t.close)
clear_output(wait=True)
display(df)
ib.pendingTickersEvent += onPendingTickers
ib.sleep(30)
ib.pendingTickersEvent -= onPendingTickers
bidSize | bid | ask | askSize | high | low | close | |
---|---|---|---|---|---|---|---|
EURUSD | 11500000 | 1.1225 | 1.12255 | 1000000 | 1.1239 | 1.11985 | 1.12 |
USDJPY | 8000000 | 108.665 | 108.675 | 16000000 | 108.885 | 108.475 | 108.87 |
GBPUSD | 6000000 | 1.327 | 1.3271 | 500000 | 1.32845 | 1.3106 | 1.3111 |
USDCHF | 10000000 | 0.9677 | 0.9678 | 7000000 | 0.9698 | 0.96465 | 0.96935 |
USDCAD | 1000000 | 1.2966 | 1.29665 | 2000000 | 1.30695 | 1.29515 | 1.3068 |
AUDUSD | 9500000 | 0.70305 | 0.7031 | 1000000 | 0.70325 | 0.69945 | 0.6995 |
New tick data is available in the 'ticks' attribute of the pending tickers. The tick data will be cleared before the next update.
To stop the live tick subscriptions:
for contract in contracts:
ib.cancelMktData(contract)
The ticks in the previous section are time-sampled by IB in order to cut on bandwidth. So with reqMktdData
not every tick from the exchanges is sent. The promise of reqTickByTickData
is to send every tick, just how it appears in the TWS Time & Sales window. This functionality is severly nerfed by a total of just three simultaneous subscriptions, where bid-ask ticks and sale ticks also use up a subscription each.
The tick-by-tick updates are available from ticker.tickByTicks
and are signalled by ib.pendingTickersEvent
or ticker.updateEvent
.
ticker = ib.reqTickByTickData(eurusd, 'BidAsk')
ib.sleep(2)
print(ticker)
ib.cancelTickByTickData(ticker.contract, 'BidAsk')
Ticker(contract=Forex('EURUSD', conId=12087792, exchange='IDEALPRO', localSymbol='EUR.USD', tradingClass='EUR.USD'), time=datetime.datetime(2019, 12, 31, 17, 5, 35, 432737, tzinfo=datetime.timezone.utc), bid=1.1225, bidSize=11000000, ask=1.1226, askSize=11500000, prevBid=1.12255, prevBidSize=11500000, prevAsk=1.12255, prevAskSize=1000000, high=1.1239, low=1.11985, close=1.12, halted=0.0, tickByTicks=[TickByTickBidAsk(time=datetime.datetime(2019, 12, 31, 17, 5, 35, 432737, tzinfo=datetime.timezone.utc), bidPrice=1.1225, askPrice=1.12255, bidSize=11500000, askSize=1000000, tickAttribBidAsk=TickAttribBidAsk(bidPastLow=False, askPastHigh=False)), TickByTickBidAsk(time=datetime.datetime(2019, 12, 31, 17, 5, 35, 432737, tzinfo=datetime.timezone.utc), bidPrice=1.1225, askPrice=1.12255, bidSize=11000000, askSize=1000000, tickAttribBidAsk=TickAttribBidAsk(bidPastLow=False, askPastHigh=False)), TickByTickBidAsk(time=datetime.datetime(2019, 12, 31, 17, 5, 35, 432737, tzinfo=datetime.timezone.utc), bidPrice=1.1225, askPrice=1.1226, bidSize=11000000, askSize=11500000, tickAttribBidAsk=TickAttribBidAsk(bidPastLow=False, askPastHigh=False))])
Historical tick data can be fetched with a maximum of 1000 ticks at a time. Either the start time or the end time must be given, and one of them must remain empty:
import datetime
start = ''
end = datetime.datetime.now()
ticks = ib.reqHistoricalTicks(eurusd, start, end, 1000, 'BID_ASK', useRth=False)
ticks[-1]
HistoricalTickBidAsk(time=datetime.datetime(2019, 12, 31, 17, 5, 34, tzinfo=datetime.timezone.utc), tickAttribBidAsk=TickAttribBidAsk(bidPastLow=False, askPastHigh=False), priceBid=1.1225, priceAsk=1.1226, sizeBid=11000000, sizeAsk=11500000)
ib.disconnect()