Placing orders in the native python IB API
This the fourth in a chain of posts on the usage of the native python API forinteractive brokers. You should study thefirst, 2nd, and 1/3, before this one.
It is an updated version ofthis older publish, which used a 3rd celebration API (swigibpy) which wraps across the C API. I've modified the code, but otherwise the publish is pretty similar.
We are nearly at the end of our journey of simplistic examples of how to get the swigibpy package to mediate between the wonderful world of Python and the dark place that is the Interactive brokers C++ API. Having learned how to get prices out of the API we are now ready to actually do some trading- submit orders, check they are active, potentially cancel them, receive fills and get historic execution data.
Where do I begin?
It's worth analyzing the relevant a part of the documentation.
You want to get the code from the following gist.
If you have a live or simulatedGateway / TWS session running (one associated with a real account number) it should work just fine.Note that you can also run this with theedemo account (password: demo123), but the results might not be reliable. This is because you are seeing the orders placed by everyone who is playing around with this account, so you could get all kinds of randomeness.
WARNINGI highly recommend that you do not run this code on a real money trading account until / unless you know exactly what you are doing! In particular real trading code should check market liquidity before trading, particularly for market orders.
Contract info - what's it?
As in the previous post there is the tedious business of creating an object to talk to IB and resolving the contract we want to trade:
app = TestApp("127.Zero.0.1", 4001, 1)
ibcontract = IBcontract() ibcontract.SecType = "FUT"
ibcontract.LastTradeDateOrContractMonth="201812"
ibcontract.Image="GE"
ibcontract.Change="GLOBEX" ## clear up the contract
resolved_ibcontract = app.Resolve_ib_contract(ibcontract)
Getting full agreement info from the server...
For the rest of the submit anything you see in that pleasant pink ambitious font is python output.
WARNING:If you are trading the VIX, which now has weekly expiries, you will need to specify the full expiry date in yyyymmdd format.
ANOTHER WARNING; the date is the contractexpiry not where relevant first or last notice date. This means you should be wary of using this date to tell you when to roll certain kinds of futures contracts eg US bonds.
Order putting - can I purchase it?
Now the moment we've all been waiting for... we're actually going to buy something. We build the order object and pass it a client function.Order1=Order() order1.Movement="BUY"
order1.OrderType="MKT"
order1.TotalQuantity=10
order1.Transmit = True orderid1 = app.Place_new_IB_order(ibcontract, order1, orderid=None) print("Placed marketplace order, orderid is %d" % orderid1)Getting orderid from IB
Using order identification of 1
Placed market order, orderid is 1
In the client class:
def place_new_IB_order(self, ibcontract, order, orderid=None): ## We can eithier deliver our own ID or ask IB to give us the following legitimate one
if orderid is None: print("Getting orderid from IB") orderid = self.Get_next_brokerorderid() if orderid is TIME_OUT: enhance Exception("I couldn't get an orderid from IB, and you didn't offer an orderid") print("Using order identification of %d" % orderid) ## Note: It's feasible if you have a couple of traidng times for orderids to be submitted out of series
## in which case IB will wreck # Place the order
self.PlaceOrder( orderid, # orderId,
ibcontract, # contract,
order # order )return orderid
Obvious first thing to notice here is the concept of an orderid. This is a number that identifies to IB what the order is; at least temporarily and for today only. Only restriction on order id's is that the next order is higher than the last. This means if you submit an order with an id of 999999 you will lose all the orderids below that. You can also reset the id 'clock' to 1 via an option on the Gateway or TWS API configuration menu. Safest thing to do is ask IB for the next orderid as done here by supplying None to the calling function.
I generate my personal orderid's who prefer to reserve them first in my very own database. This is satisfactory as long as you are walking a single linear manner where there's no threat of an 'older' order being submitted earlier than a 'more moderen' one.
Fill records - how an awful lot did it cost me?
What happens when an order is filled; completely or partially? Well the following method in the wrapper function is triggered. Notice that the logic is slightly more complicated because this function fulfills two duties:- A fill has arrived when a trade actually executes, in which case the reqId will be -1 (stored as FILL_CODE here)
- We canask for fill information, in which case the reqId will accompany the information. More on this shortly
def execDetails(self, reqId, settlement, execution): ## overriden method execdata = execInformation(execution.ExecId, contract=settlement, ClientId=execution.ClientId, OrderId=execution.OrderId, time=execution.Time, AvgPrice=execution.AvgPrice, AcctNumber=execution.AcctNumber, Shares=execution.Stocks, Price = execution.Price) ## there are some different matters in execution you could upload
## make certain you upload them to the .Attributes() discipline of the execInformation class reqId = int(reqId) ## We eithier positioned this right into a move if its simply came about, or shop it for a specific request
if reqId==FILL_CODE: self._my_executions_stream.Put(execdata) else: self._my_requested_execution[reqId].Put(execdata)
In this situation as we haven't but requested for execution information reqId might be -1, and we will placed the statistics into my_executions_stream. This is a 'everlasting' queue that hosts execution records: I describe it as permanent because we failed to need to run an init_ ... Technique to set it up in place_new_IB_order like I've shown you in preceding posts; the queue is created while the example of TestWrapper is created.
(Also this will be the characteristic that might update your order status database; as a pro at these items, certainly you would have one of these element).
To see the fill facts I want to do that:
print("Recent fills") filldetails = app.Recent_fills_and_commissions() print(filldetails)There is quite a lot going on below the hood here that is well worth know-how so we could take a look at the patron function we're calling.
Def recent_fills_and_commissions(self): """ Return recent fills, with commissions delivered in :go back: dict of execInformation objects, keys are execids
""" recent_fills = self._recent_fills() commissions = self._all_commissions() ## we want all commissions
## glue them collectively, create a dict, remove duplicates
all_data = recent_fills.Blended_dict(commissions) return all_data
The first element to word is that this returns statistics coming from two assets: execDetails (labelled recent_fills right here) and commissions. Frustratingly this records is supplied by using separate components of the wrapper feature. We've already visible the executions, here it's far for commissions:
def commissionReport(self, commreport): commdata = execInformation(commreport.ExecId, Commission=commreport.Fee, commission_currency = commreport.Currency, realisedpnl = commreport.RealizedPNL) ## there are a few different matters in commreport you may upload
## ensure you add them to the .Attributes() subject of the execInformation magnificence ## These continually move into the 'flow' as may be from a request,
## or a fill thats simply came about
self._my_commission_stream.Positioned(commdata)
As with execution details this can get called eithier when we've requested execution details (of which more later), or whenever we get an actual fill (as we're doing here). Unlike the execDetails function however we never get a reqId. So all the commissions data is dumped into a 'permanent' _stream queue, rather than one we've initialised already.
Once the executions and commission records has arrived in the 'everlasting' _stream queues, we will access it through those features:
def _recent_fills(self): """ Returns any fills seeing that we remaining called recent_fills :go back: list of executions as execInformation objects
""" ## we don't set up a queue however access the permanent one
fill_queue = self.Access_executions_stream() list_of_fills=list_of_execInformation() at the same time as not fill_queue.Empty(): MAX_WAIT_SECONDS = five
strive: next_fill = fill_queue.Get(timeout=MAX_WAIT_SECONDS) list_of_fills.Append(next_fill) except queue.Empty: ## corner case where Q emptied since we ultimate checked if empty at pinnacle of while loop
skip ## notice this could encompass duplicates and is a listing
go back list_of_fills
Notice that we clear the queue once we've accessed the data. And _recent_commissions(self) is in a similar vein.
Def _recent_commissions(self): ## we don't set up a queue, as there is a everlasting one
comm_queue = self.Access_commission_stream()
list_of_comm=list_of_execInformation() even as not comm_queue.Empty(): MAX_WAIT_SECONDS = five
try: next_comm = comm_queue.get(timeout=MAX_WAIT_SECONDS) list_of_comm.append(next_comm) except queue.Empty: ## corner case where Q emptied since we last checked if empty at top of while loop skip ## notice this could encompass duplicates and is a listing
return list_of_comm
However due to the fact all commissions stay inside the commission_stream queue I certainly use every other feature to get fee facts, which returns both the remaining bite of commissions, plus some other fee data I've gathered:
def _all_commissions(self):
## self._commissions is created whilst the purchaser example is __init__
original_commissions = self._commissions
latest_commissions = self._recent_commissions() ## these are just easy lists so we are able to glue them collectively all_commissions = list_of_execInformation(original_commissions latest_commissions) self._commissions = all_commissions # word this can include duplicates and is a list
go back all_commissions
Finally we must glue these together. The final line inside the function earlier than the go back populates the fill records with the relevant commission degrees.
Def recent_fills_and_commissions(self):
recent_fills = self._recent_fills()
commissions = self._all_commissions() ## we want all commissions
## glue them collectively, create a dict, remove duplicates
all_data = recent_fills.Blended_dict(commissions) return all_data
Recent fills
{'00004468.58ca0e5f.01.01': Execution - contract: 56825063,GE,FUT,20181217,0.0,,2500,GLOBEX,,USD,GEZ8,GE,False,,combo: ClientId: 1 OrderId: 1 time: 20170316 09:50:31 AvgPrice: 98.055 Price: 98.055 AcctNumber: DU15075 Shares: 10.0 Commission: 24.0 commission_currency: USD realisedpnl: 1.7976931348623157e+308}
Notice this returns a dict; the keyword is the execId. I use a few easy gadgets to acquire up and merge collectively these streams of statistics however you may do it otherwise of direction.
Just to reiterate the _recent features clean the relevant queues as you can see when I try to get the fill facts once more (although the commission data is stored so self._all_commissions() will still work).
## when I call once more need to be empty as we've got cleared the memory of latest fills
print("Recent fills (should be clean)") morefilldetails = app.recent_fills_and_commissions() print(morefilldetails)
Recent fills (should be clean)
IMPORTANT DETAIL:It won't be obvious from this simple example unless you can submit a very large order in a thin market but the fills come in ascumulative order updates, not separate fills. Its worth looking at an example. Suppose you try and buy 10 lots, and you get fills of:
- 3 lots @ 100.0
- 6 lots @ 100.2
- 1 lot @ 100.5
- qty: 3 lots, price=100.0
- qty: 9 lots, price=100.13333333
- qty: 10 lots, price=100.17
By the manner 'orderid' is only a temporary aspect for IB; after tommorrow it won't associate it with this order. Instead you need to use 'permid' to your report retaining. 'execid' is extraordinary for each component fill so that you could use it to ensure you are not including fill facts you have already got; in exercise this isn't always complicated due to the cumulative nature of the statistics.
It is simply very vital that fill facts is effectively captured by your trading software. One reason being to maintain tune of what your function is; as we shall see in the next put up IB would not offer mere mortals a first rate correct present day function facility. So I typically use my very own information of exchange records to decide wherein I am, function smart. Because the fills usually arrive inside the wrapper characteristic best as soon as its possible under sure conditions to miss them; eg in case your API client dies before you notice the fill or just is not running when one arrives on a formerly closed market within the middle of the night. Its generally proper exercise then to reconcile what IB has for a report of fills versus your own.
This records is handiest to be had as much as nighttime of the day you alternate. So I run a reconciliation three times a day. If you lose a fill from earlier than nowadays you will want to find it at the IB internet site account management microsite, and manually enter it into your database.
Here is how we do it.
This is a greater familiar pattern: we create a queue for the wrapper to position stuff so that it will be terminated through a end event, and then request the IB API to begin sending records, some technique in the wrapper populates the queue, and we then pull the contents of the queue in. The relevant wrapper function should appearance familiar:
(Note: We can trade ExecutionFilter to subset exclusive orders)
def execDetails(self, reqId, settlement, execution): ## overriden method execdata = execInformation(execution.ExecId, contract=settlement, ClientId=execution.ClientId, OrderId=execution.OrderId, time=execution.Time, AvgPrice=execution.AvgPrice, AcctNumber=execution.AcctNumber, Shares=execution.Stocks, Price = execution.Price) ## there are some different matters in execution you could upload
## make certain you upload them to the .Attributes() discipline of the execInformation class reqId = int(reqId) ## We eithier positioned this right into a move if its simply came about, or shop it for a specific request
if reqId==FILL_CODE: self._my_executions_stream.Put(execdata) else: self._my_requested_execution[reqId].Put(execdata)
Its the same hardworking function as earlier than, most effective this time the reqId will no longer be -1 (acting right here as FILL_CODE in place of being hardcoded) so we append the fill this is acquired to the requested queue for a given reqId.
The simplest wrinkle is that the fee statistics needs to be brought in and merged, as we did before with fills:
execdetails = app.Get_executions_and_commissions() print(execdetails)
{'00004468.58ca0e5f.01.01': Execution - contract: 56825063,GE,FUT,20181217,0.0,,2500,GLOBEX,,USD,GEZ8,GE,False,,combo: ClientId: 1 OrderId: 1 time: 20170316 09:50:31 AvgPrice: 98.055 Price: 98.055 AcctNumber: DU15075 Shares: 10.0 Commission: 24.0 commission_currency: USD realisedpnl: 1.7976931348623157e+308}
As earlier than the executions are indexed in a dict with the execId as the key.
Getting orderid from IB
Placing restrict orders
No self respecting trader will use marketplace orders (see
my publish), so the way to define restriction orders?
Order2=Order() order2.Motion="SELL"
order2.orderType="LMT"
order2.totalQuantity=12
order2.lmtPrice = 100.0
order2.tif = 'DAY'
order2.transmit = True orderid2 = app.place_new_IB_order(ibcontract, order2, orderid=None) print("Placed limit order, orderid is %d" % orderid2)
Using order identification of two
Placed limit order, orderid is 2
This is only a tiny choice of the to be had orders, see the medical doctors right here.
Active order reputation- have I offered it?
IB can tell us what orders we are working. Unless you ask very quickly (or submit your order outside of trading hours) this is likely only to return unfilled limit orders like the one we've just submitted.Open_orders = app.Get_open_orders() print(open_orders)
Client characteristic:
def get_open_orders(self): ## store the orders someplace
open_orders_queue = finishableQueue(self.Init_open_orders()) ## You can also opt to use reqOpenOrders() which most effective retrieves orders for this consumer self.ReqAllOpenOrders() ## Run until we get a terimination or lose interest waiting
MAX_WAIT_SECONDS = 5
open_orders_list = list_of_orderInformation(open_orders_queue.Get(timeout = MAX_WAIT_SECONDS)) while self.Wrapper.Is_error(): print(self.Get_error()) if open_orders_queue.Timed_out(): print("Exceeded maximum await wrapper to confirm completed whilst getting orders") ## open orders queue can be a jumble of order details, turn into a tidy dict and not using a duplicates open_orders_dict = open_orders_list.Merged_dict() go back open_orders_dictWrapper features:
def init_open_orders(self): open_orders_queue = self._my_open_orders = queue.Queue() return open_orders_queue def orderStatus(self, orderId, status, filled, final, avgFillPrice, permid, parentId, lastFillPrice, clientId, whyHeld): order_details = orderInformation(orderId, fame=popularity, crammed=stuffed, avgFillPrice=avgFillPrice, permid=permid, parentId=parentId, lastFillPrice=lastFillPrice, clientId=clientId, whyHeld=whyHeld) self._my_open_orders.Positioned(order_details) def openOrder(self, orderId, settlement, order, orderstate): order_details = orderInformation(orderId, contract=settlement, order=order,
orderstate = orderstate) self._my_open_orders.Positioned(order_details) def openOrderEnd(self):
self._my_open_orders.Put(FINISHED)
{2: Order - contract: 56825063,GE,FUT,20181217,0.0,?,2500,GLOBEX,,USD,GEZ8,GE,False,,combo: order: 2,1,1017786930: LMT SELL 12@100.000000 DAY orderstate: <ibapi.order_state.OrderState object at 0x7f5e7bcfe860> status: Submitted filled: 0.0 avgFillPrice: 0.0 permid: 1017786930 parentId: 0 lastFillPrice: 0.0 clientId: 1 whyHeld: }
This should be a familiar story now. The only interesting thing is that we get order details from two wrapper functions, orderStatus and openOrder. I don't bother segregating them, instead I add them to the same queue, and then merge them with open_orders_dict = open_orders_list.merged_dict()
In exercise I even have noticed that the appropriate stop condition for receiving open orders doesn't constantly cause so you do want an max waiting time (which is ideal exercise besides).
As with fills the output is back as a dict, and the keys are orderids. We just have the single energetic limit order because the marketplace order (orderid 1) has lengthy given that been stuffed.
Order modification
To regulate an present order we submit a brand new order, but with an existing orderid.
Order3=Order() order3.Movement="BUY"
order3.OrderType="LMT"
order3.TotalQuantity=5
order3.LmtPrice = 10.Zero
order3.Tif = 'DAY'
order3.Transmit = True orderid3 = app.Place_new_IB_order(ibcontract, order3, orderid=None) print("Placed limit order, orderid is %d" % orderid2) print("Open orders (need to be two)") open_orders = app.Get_open_orders() print(open_orders.Keys())Getting orderid from IB
Using order identification of 3
Placed limit order, orderid is 2
Open orders (have to be )
dict_keys([2, 3])
print("Modifying order %d" % orderid3) order3.LmtPrice = 15.Zero print("Limit price turned into %f turns into %f" % (open_orders[orderid3].Order.LmtPrice, order3.LmtPrice )) app.Place_new_IB_order(ibcontract, order3, orderid=orderid3) time.Sleep(5) open_orders = app.Get_open_orders() print("New restrict fee %f " % open_orders[orderid3].Order.LmtPrice)Modifying order 3
Limit charge become 10.000000 will become 15.000000
New restriction rate 15.000000
Its counseled that you only exchange the quantity and restriction price (where relevant) of an order; not the order type.
Order cancelling - what if I do not need it any greater?
Cancelling an order is quite simple:
Cancel order 2
IB mistakes identification 2 errorcode 202 string Order Canceled - reason:
My code assessments that the order has been cancelled:
def cancel_order(self, orderid):
self.CancelOrder(orderid) ## Wait till order is cancelled start_time=datetime.Datetime.Now() MAX_WAIT_TIME_SECONDS = 10 finished = False whilst no longer finished: if orderid now not in self.Get_open_orders(): ## sooner or later cancelled
finished = True if (datetime.datetime.now() - start_time).seconds > MAX_WAIT_TIME_SECONDS: print("Wrapper didn't come back with confirmation that order was cancelled!") finished = True ## return nothing
... but you can also check yourself:print("Open orders (have to just be %d)" % orderid3) print(open_orders.Keys())Open orders (should simply be 3)
dict_keys([3])
Note this will only cancel orders where the clientid (the third number in this call app = TestApp("127.Zero.0.1", 4001, 1)) is the same as the current client. To cancel all orders, regardless of the client:
print("Cancelling all orders") app.Cancel_all_orders() print("Any open orders? - must be False") print(app.Any_open_orders())Cancelling all orders
IB blunders identification three errorcode 202 string Order Canceled - reason:
Open orders? - need to be False
False
Are we completed?
Yes, as a minimum with this publish. The last element I will display you a way to do is to get accounting statistics, so the tedium is almost over.
This is the fourth in a chain of five posts on building a simple interface in python to the IB API using swigiby. The first 3 posts are:
http://qoppac.Blogspot.Co.Uk/2017/03/interactive-brokers-local-python-api.Html
http://qoppac.Blogspot.Co.United kingdom/2017/03/historical-information-from-native-ib-pyhon-api.Html
http://qoppac.Blogspot.Co.United kingdom/2017/03/streaming-market-records-from-native.Html
The subsequent, and very last, post is:
http://qoppac.Blogspot.Co.Uk/2017/03/getting-role-and-accounting-information.Html