Let's first take a look at what's inside the ib_insync
package:
import ib_insync
print(ib_insync.__all__)
['util', 'Event', 'OrderComboLeg', 'OrderState', 'SoftDollarTier', 'PriceIncrement', 'Execution', 'CommissionReport', 'BarList', 'BarDataList', 'RealTimeBarList', 'BarData', 'RealTimeBar', 'HistogramData', 'NewsProvider', 'DepthMktDataDescription', 'ScannerSubscription', 'ScanDataList', 'FundamentalRatios', 'ExecutionFilter', 'PnL', 'PnLSingle', 'AccountValue', 'TickData', 'TickByTickAllLast', 'TickByTickBidAsk', 'TickByTickMidPoint', 'HistoricalTick', 'HistoricalTickBidAsk', 'HistoricalTickLast', 'TickAttrib', 'TickAttribBidAsk', 'TickAttribLast', 'MktDepthData', 'DOMLevel', 'TradeLogEntry', 'TagValue', 'FamilyCode', 'SmartComponent', 'PortfolioItem', 'Position', 'Fill', 'OptionComputation', 'OptionChain', 'Dividends', 'NewsArticle', 'HistoricalNews', 'NewsTick', 'NewsBulletin', 'ConnectionStats', 'Contract', 'Stock', 'Option', 'Future', 'ContFuture', 'Forex', 'Index', 'CFD', 'Commodity', 'Bond', 'FuturesOption', 'MutualFund', 'Warrant', 'Bag', 'ComboLeg', 'DeltaNeutralContract', 'ContractDetails', 'ContractDescription', 'ScanData', 'Trade', 'OrderStatus', 'Order', 'LimitOrder', 'MarketOrder', 'StopOrder', 'StopLimitOrder', 'BracketOrder', 'OrderCondition', 'ExecutionCondition', 'MarginCondition', 'TimeCondition', 'PriceCondition', 'PercentChangeCondition', 'VolumeCondition', 'Ticker', 'IB', 'Client', 'Wrapper', 'FlexReport', 'FlexError', 'IBC', 'IBController', 'Watchdog']
The following two lines are used at the top of all notebooks. The first line imports everything and the second starts an event loop to keep the notebook live updated:
from ib_insync import *
util.startLoop()
Note that startLoop() only works in notebooks, not in regular Python programs.
The main player of the whole package is the "IB" class. Let's create an IB instance and connect to a running TWS/IBG application:
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=10)
<IB connected to 127.0.0.1:7497 clientId=10>
If the connection failed, then verify that the application has the API port enabled and double-check the hostname and port. For IB Gateway the default port is 4002. Make sure the clientId is not already in use.
If the connection succeeded, then ib will be synchronized with TWS/IBG. The "current state" is now available via methods such as ib.positions(), ib.trades(), ib.openTrades(), ib.accountValues() or ib.tickers(). Let's list the current positions:
ib.positions()
[]
Or filter the account values to get the liquidation value:
[v for v in ib.accountValues() if v.tag == 'NetLiquidationByCurrency' and v.currency == 'BASE']
[AccountValue(account='U172554', tag='NetLiquidationByCurrency', value='2039.4021', currency='BASE', modelCode='')]
The "current state" will automatically be kept in sync with TWS/IBG. So an order fill will be added as soon as it is reported, or account values will be updated as soon as they change in TWS.
Contracts can be specified in different ways:
Some examples:
Contract(conId=270639)
Stock('AMD', 'SMART', 'USD')
Stock('INTC', 'SMART', 'USD', primaryExchange='NASDAQ')
Forex('EURUSD')
CFD('IBUS30')
Future('ES', '20180921', 'GLOBEX')
Option('SPY', '20170721', 240, 'C', 'SMART')
Bond(secIdType='ISIN', secId='US03076KAA60');
The IB class has nearly all request methods that the IB API offers. The methods that return a result will block until finished and then return the result. Take for example reqContractDetails:
contract = Stock('TSLA', 'SMART', 'USD')
ib.reqContractDetails(contract)
[ContractDetails(contract=Contract(secType='STK', conId=76792991, symbol='TSLA', exchange='SMART', primaryExchange='NASDAQ', currency='USD', localSymbol='TSLA', tradingClass='NMS', comboLegs=[]), marketName='NMS', minTick=0.01, orderTypes='ACTIVETIM,ADJUST,ALERT,ALGO,ALLOC,AON,AVGCOST,BASKET,BENCHPX,COND,CONDORDER,DARKONLY,DARKPOLL,DAY,DEACT,DEACTDIS,DEACTEOD,DIS,GAT,GTC,GTD,GTT,HID,IBKRATS,ICE,IMB,IOC,LIT,LMT,LOC,MIDPX,MIT,MKT,MOC,MTL,NGCOMB,NODARK,NONALGO,OCA,OPG,OPGREROUT,PEGBENCH,POSTONLY,PREOPGRTH,REL,RPI,RTH,SCALE,SCALEODD,SCALERST,SNAPMID,SNAPMKT,SNAPREL,STP,STPLMT,SWEEP,TRAIL,TRAILLIT,TRAILLMT,TRAILMIT,WHATIF', validExchanges='SMART,AMEX,NYSE,CBOE,PHLX,ISE,CHX,ARCA,ISLAND,DRCTEDGE,BEX,BATS,EDGEA,CSFBALGO,JEFFALGO,BYX,IEX,EDGX,FOXRIVER,TPLUS1,NYSENAT,PSX', priceMagnifier=1, underConId=0, longName='TESLA INC', contractMonth='', industry='Consumer, Cyclical', category='Auto Manufacturers', subcategory='Auto-Cars/Light Trucks', timeZoneId='EST (Eastern Standard Time)', tradingHours='20191231:0400-20191231:2000;20200101:CLOSED;20200102:0400-20200102:2000;20200103:0400-20200103:2000;20200104:CLOSED;20200105:CLOSED;20200106:0400-20200106:2000;20200107:0400-20200107:2000;20200108:0400-20200108:2000;20200109:0400-20200109:2000;20200110:0400-20200110:2000;20200111:CLOSED;20200112:CLOSED;20200113:0400-20200113:2000;20200114:0400-20200114:2000;20200115:0400-20200115:2000;20200116:0400-20200116:2000;20200117:0400-20200117:2000;20200118:CLOSED;20200119:CLOSED;20200120:0400-20200120:2000;20200121:0400-20200121:2000;20200122:0400-20200122:2000;20200123:0400-20200123:2000;20200124:0400-20200124:2000;20200125:CLOSED;20200126:CLOSED;20200127:0400-20200127:2000;20200128:0400-20200128:2000;20200129:0400-20200129:2000;20200130:0400-20200130:2000;20200131:0400-20200131:2000;20200201:CLOSED;20200202:CLOSED;20200203:0400-20200203:2000', liquidHours='20191231:0930-20191231:1600;20200101:CLOSED;20200102:0930-20200102:1600;20200103:0930-20200103:1600;20200104:CLOSED;20200105:CLOSED;20200106:0930-20200106:1600;20200107:0930-20200107:1600;20200108:0930-20200108:1600;20200109:0930-20200109:1600;20200110:0930-20200110:1600;20200111:CLOSED;20200112:CLOSED;20200113:0930-20200113:1600;20200114:0930-20200114:1600;20200115:0930-20200115:1600;20200116:0930-20200116:1600;20200117:0930-20200117:1600;20200118:CLOSED;20200119:CLOSED;20200120:0930-20200120:1600;20200121:0930-20200121:1600;20200122:0930-20200122:1600;20200123:0930-20200123:1600;20200124:0930-20200124:1600;20200125:CLOSED;20200126:CLOSED;20200127:0930-20200127:1600;20200128:0930-20200128:1600;20200129:0930-20200129:1600;20200130:0930-20200130:1600;20200131:0930-20200131:1600;20200201:CLOSED;20200202:CLOSED;20200203:0930-20200203:1600', evRule='', evMultiplier=0, mdSizeMultiplier=100, aggGroup=1, underSymbol='', underSecType='', marketRuleIds='26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26', secIdList=[], realExpirationDate='', lastTradeTime='', stockType='COMMON', cusip='', ratings='', descAppend='', bondType='', couponType='', callable=False, putable=False, coupon=0, convertible=False, maturity='', issueDate='', nextOptionDate='', nextOptionType='', nextOptionPartial=False, notes='')]
Doing a request involves network traffic going up and down and can take considerable time. The current state on the other hand is always immediately available. So it is preferable to use the current state methods over requests. For example, use ib.openOrders()
in preference over ib.reqOpenOrders()
, or ib.positions()
over ib.reqPositions()
, etc:
%time l = ib.positions()
CPU times: user 5 µs, sys: 3 µs, total: 8 µs Wall time: 9.06 µs
%time l = ib.reqPositions()
CPU times: user 0 ns, sys: 745 µs, total: 745 µs Wall time: 32.7 ms
The following will put log messages of INFO and higher level under the current active cell:
util.logToConsole()
To see all debug messages (including network traffic):
import logging
util.logToConsole(logging.DEBUG)
The following will disconnect ib
and clear all its state:
ib.disconnect()
2019-12-31 13:28:29,252 ib_insync.ib INFO Disconnecting from 127.0.0.1:7497, 160 B sent in 9 messages, 21.0 kB received in 418 messages, session time 920 ms. 2019-12-31 13:28:29,255 ib_insync.client INFO Disconnecting