Tick data

For optimum results this notebook should be run during the Forex trading session.

In [1]:
from ib_insync import *
util.startLoop()

ib = IB()
ib.connect('127.0.0.1', 7497, clientId=15)
Out[1]:
<IB connected to 127.0.0.1:7497 clientId=15>

Streaming tick data

Create some Forex contracts:

In [2]:
contracts = [Forex(pair) for pair in ('EURUSD', 'USDJPY', 'GBPUSD', 'USDCHF', 'USDCAD', 'AUDUSD')]
ib.qualifyContracts(*contracts)

eurusd = contracts[0]

Request streaming ticks for them:

In [3]:
for contract in contracts:
    ib.reqMktData(contract, '', False, False)

Wait a few seconds for the tickers to get filled.

In [4]:
ticker = ib.ticker(eurusd)
ib.sleep(2)

ticker
Out[4]:
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:

In [5]:
ticker.marketPrice()
Out[5]:
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.

In [6]:
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:

In [7]:
for contract in contracts:
    ib.cancelMktData(contract)

Tick by Tick data

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.

In [8]:
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

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:

In [9]:
import datetime

start = ''
end = datetime.datetime.now()
ticks = ib.reqHistoricalTicks(eurusd, start, end, 1000, 'BID_ASK', useRth=False)

ticks[-1]
Out[9]:
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)
In [10]:
ib.disconnect()