Ordering

Warning: This notebook will place live orders

Use a paper trading account (during market hours).

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

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

Create a contract and a market order:

In [2]:
contract = Forex('EURUSD')
ib.qualifyContracts(contract)

order = LimitOrder('SELL', 20000, 1.11)

placeOrder will place the order order and return a Trade object right away (non-blocking):

In [3]:
trade = ib.placeOrder(contract, order)

trade contains the order and everything related to it, such as order status, fills and a log. It will be live updated with every status change or fill of the order.

In [4]:
ib.sleep(1)
trade.log
Out[4]:
[TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 7, 96198, tzinfo=datetime.timezone.utc), status='PendingSubmit', message=''),
 TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 7, 283531, tzinfo=datetime.timezone.utc), status='PreSubmitted', message=''),
 TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 7, 323689, tzinfo=datetime.timezone.utc), status='PreSubmitted', message='Fill [email protected]'),
 TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 7, 323689, tzinfo=datetime.timezone.utc), status='Filled', message='')]

trade will also available from ib.trades():

In [5]:
assert trade in ib.trades()

Likewise for order:

In [6]:
assert order in ib.orders()

Now let's create a limit order with an unrealistic limit:

In [7]:
limitOrder = LimitOrder('BUY', 20000, 0.05)
limitTrade = ib.placeOrder(contract, limitOrder)

limitTrade
Out[7]:
Trade(contract=Forex('EURUSD', conId=12087792, exchange='IDEALPRO', localSymbol='EUR.USD', tradingClass='EUR.USD'), order=LimitOrder(orderId=23, clientId=13, action='BUY', totalQuantity=20000, lmtPrice=0.05), orderStatus=OrderStatus(orderId=0, status='PendingSubmit', filled=0, remaining=0, avgFillPrice=0.0, permId=0, parentId=0, lastFillPrice=0.0, clientId=0, whyHeld='', mktCapPrice=0.0, lastLiquidity=0), fills=[], log=[TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 8, 149188, tzinfo=datetime.timezone.utc), status='PendingSubmit', message='')])

status will change from "PendingSubmit" to "Submitted":

In [8]:
ib.sleep(1)
assert limitTrade.orderStatus.status == 'Submitted'
In [9]:
assert limitTrade in ib.openTrades()

Let's modify the limit price and resubmit:

In [10]:
limitOrder.lmtPrice = 0.10

ib.placeOrder(contract, limitOrder)
Out[10]:
Trade(contract=Forex('EURUSD', conId=12087792, exchange='IDEALPRO', localSymbol='EUR.USD', tradingClass='EUR.USD'), order=LimitOrder(orderId=23, clientId=13, permId=1710981560, action='BUY', totalQuantity=20000.0, lmtPrice=0.1, auxPrice=0.0, tif='DAY', ocaType=3, trailStopPrice=1.05, volatilityType=0, deltaNeutralOrderType='None', referencePriceType=0, account='DU772802', clearingIntent='IB', adjustedOrderType='None', cashQty=0.0, dontUseAutoPriceForHedge=True), orderStatus=OrderStatus(orderId=0, status='Submitted', filled=0.0, remaining=20000.0, avgFillPrice=0.0, permId=1710981560, parentId=0, lastFillPrice=0.0, clientId=13, whyHeld='', mktCapPrice=0.0, lastLiquidity=0), fills=[], log=[TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 8, 149188, tzinfo=datetime.timezone.utc), status='PendingSubmit', message=''), TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 8, 249250, tzinfo=datetime.timezone.utc), status='Submitted', message=''), TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 9, 176924, tzinfo=datetime.timezone.utc), status='Submitted', message='Modify')])

And now cancel it:

In [11]:
ib.cancelOrder(limitOrder)
Out[11]:
Trade(contract=Forex('EURUSD', conId=12087792, exchange='IDEALPRO', localSymbol='EUR.USD', tradingClass='EUR.USD'), order=LimitOrder(orderId=23, clientId=13, permId=1710981560, action='BUY', totalQuantity=20000.0, lmtPrice=0.1, auxPrice=0.0, tif='DAY', ocaType=3, trailStopPrice=1.05, volatilityType=0, deltaNeutralOrderType='None', referencePriceType=0, account='DU772802', clearingIntent='IB', adjustedOrderType='None', cashQty=0.0, dontUseAutoPriceForHedge=True), orderStatus=OrderStatus(orderId=0, status='PendingCancel', filled=0.0, remaining=20000.0, avgFillPrice=0.0, permId=1710981560, parentId=0, lastFillPrice=0.0, clientId=13, whyHeld='', mktCapPrice=0.0, lastLiquidity=0), fills=[], log=[TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 8, 149188, tzinfo=datetime.timezone.utc), status='PendingSubmit', message=''), TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 8, 249250, tzinfo=datetime.timezone.utc), status='Submitted', message=''), TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 9, 176924, tzinfo=datetime.timezone.utc), status='Submitted', message='Modify'), TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 9, 183117, tzinfo=datetime.timezone.utc), status='PendingCancel', message='')])
In [12]:
limitTrade.log
Out[12]:
[TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 8, 149188, tzinfo=datetime.timezone.utc), status='PendingSubmit', message=''),
 TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 8, 249250, tzinfo=datetime.timezone.utc), status='Submitted', message=''),
 TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 9, 176924, tzinfo=datetime.timezone.utc), status='Submitted', message='Modify'),
 TradeLogEntry(time=datetime.datetime(2019, 12, 31, 17, 19, 9, 183117, tzinfo=datetime.timezone.utc), status='PendingCancel', message='')]

placeOrder is not blocking and will not wait on what happens with the order. To make the order placement blocking, that is to wait until the order is either filled or canceled, consider the following:

In [13]:
%%time
order = MarketOrder('BUY', 100)

trade = ib.placeOrder(contract, order)
while not trade.isDone():
    ib.waitOnUpdate()
CPU times: user 5.87 ms, sys: 486 ┬Ás, total: 6.35 ms
Wall time: 163 ms

What are our positions?

In [14]:
ib.positions()
Out[14]:
[Position(account='DU772802', contract=Stock(conId=9579970, symbol='IWM', exchange='ARCA', currency='USD', localSymbol='IWM', tradingClass='IWM'), position=600.0, avgCost=149.368),
 Position(account='DU772802', contract=Forex('USDCHF', conId=12087820, localSymbol='USD.CHF', tradingClass='USD.CHF'), position=20.0, avgCost=0.98545),
 Position(account='DU772802', contract=Forex('EURUSD', conId=12087792, localSymbol='EUR.USD', tradingClass='EUR.USD'), position=165700.0, avgCost=1.1247148664589277),
 Position(account='DU772802', contract=Stock(conId=265598, symbol='AAPL', exchange='NASDAQ', currency='USD', localSymbol='AAPL', tradingClass='NMS'), position=2551.0, avgCost=187.1202698)]

What's the total of commissions paid today?

In [15]:
sum(fill.commissionReport.commission for fill in ib.fills())
Out[15]:
11.607050000000001

whatIfOrder can be used to see the commission and the margin impact of an order without actually sending the order:

In [16]:
order = MarketOrder('SELL', 20000)
ib.whatIfOrder(contract, order)
Out[16]:
OrderState(status='PreSubmitted', initMarginBefore='188130.0', maintMarginBefore='188130.0', equityWithLoanBefore='1126836.45', initMarginChange='0.0', maintMarginChange='0.0', equityWithLoanChange='-1.790000000037253', initMarginAfter='188130.0', maintMarginAfter='188130.0', equityWithLoanAfter='1126834.66', commission=1.7857, minCommission=1.7976931348623157e+308, maxCommission=1.7976931348623157e+308, commissionCurrency='EUR', warningText='', completedTime='', completedStatus='')
In [17]:
ib.disconnect()