A while back we discussed how to set up an Interactive Brokers demo account. Interactive Brokers is one of the main brokerages used by retail algorithmic traders due to its relatively low minimal account balance requirements (10,000 USD) and (relatively) straightforward API. In this article we will make use of a demo account to automate trades against the Interactive Brokers API, via Python and the IBPy plugin.
Disclosure: I have no affiliation with Interactive Brokers. I have used them before in a professional fund context and as such am familiar with their software.
The Interactive Brokers API
Interactive Brokers is a large enterprise and as such caters to a wide-range of traders, ranging from discretionary retail to automated institutional. This has led their GUI interface, Trader Workstation (TWS), to possess a significant quantity of "bells and whistles".
In addition to TWS there is also a lightweight component called the IB Gateway, which provides the same access to the IB servers, albeit without the extra functionality of the GUI. For our automated trading purposes we won't actually need the TWS GUI, but I think for this tutorial it is demonstrative to make use of it.
The underlying architecture is based on a client/server model which offers both execution and market data feeds (historical and real-time) via an API. It is this API that we will utilise in this tutorial to send automated orders, via IBPy.
IBPy has been written to "wrap" the native Java API and make it straightforward to call from Python. The two main libraries we are interested in within IBPy are
ib.opt. The latter is higher level and makes use of functionality in the former.
In the following implementation we are going to create an extremely simple example, which will simply send a single market order to buy 100 units of Google stock, using smart order routing. The latter is designed to achieve the best price in practice, although in certain situations it can be suboptimal. However for the purposes of this tutorial it will suffice.
Implementation in Python
Before we begin it is necessary to have followed the steps in the prior tutorial on setting up an Interactive Brokers account. In addition it is necessary to have a prior Python workspace so that we can install IBPy, which will allow you to tie other aspects of your code together. The tutorial on installing a Python research environment will create the necessary workspace.
IBPy is a Python wrapper written around the Java-based Interactive Brokers API. It makes development of algorithmic trading systems in Python somewhat less problematic. It will be used as the basis for all subsequent communication with Interactive Brokers until we consider the FIX protocol at a later date.
sudo apt-get install git-core
Once you have git installed you can create a subdirectory to store IBPy. On my system I have simply placed it underneath my home directory:
The next step is to download IBPy via
cd ~/ibapi git clone https://github.com/blampe/IbPy
Make sure to enter the IbPy directory and install with the preferred Python virtual environment:
cd ~/ibapi/IbPy python setup.py.in install
That completes the installation of IBPy. The next step is to open up TWS (as described in the prior tutorial).
TWS Portfolio View (Prior to Google Trade)
The following code will demonstrate an extremely simple API-based order mechanism. The code is far from production-ready but it does demonstrate the essential functionality of the Interactive Brokers API and how to use it for order execution.
All of the following code should reside in the
ib_api_demo.py file. The first step is to import the
Order objects from the lower level
ib.ext library. In addition we import the
message objects from the
ib.opt higher level library:
# ib_api_demo.py from ib.ext.Contract import Contract from ib.ext.Order import Order from ib.opt import Connection, message
IB provides us with the capability of handling errors and server responses by a callback mechanism. The following two functions do nothing more than print out the contents of the messages returned from the server. A more sophisticated production system would have to implement logic to ensure continual running of the system in the event of exceptional behaviour:
# ib_api_demo.py def error_handler(msg): """Handles the capturing of error messages""" print "Server Error: %s" % msg def reply_handler(msg): """Handles of server replies""" print "Server Response: %s, %s" % (msg.typeName, msg)
The following two functions wrap the creation of the
Order objects, setting their respective parameters. The function docs describe each parameter individually:
# ib_api_demo.py def create_contract(symbol, sec_type, exch, prim_exch, curr): """Create a Contract object defining what will be purchased, at which exchange and in which currency. symbol - The ticker symbol for the contract sec_type - The security type for the contract ('STK' is 'stock') exch - The exchange to carry out the contract on prim_exch - The primary exchange to carry out the contract on curr - The currency in which to purchase the contract""" contract = Contract() contract.m_symbol = symbol contract.m_secType = sec_type contract.m_exchange = exch contract.m_primaryExch = prim_exch contract.m_currency = curr return contract def create_order(order_type, quantity, action): """Create an Order object (Market/Limit) to go long/short. order_type - 'MKT', 'LMT' for Market or Limit orders quantity - Integral number of assets to order action - 'BUY' or 'SELL'""" order = Order() order.m_orderType = order_type order.m_totalQuantity = quantity order.m_action = action return order
__main__ function initially creates a
Connection object to Trader Workstation, which must be running for the code to function. The error and reply handler functions are then registered with the connection object. Subsequently an
order_id variable is defined. In a production system this must be incremented for each trade order.
The next steps are to create a
Contract and an
Order representing a market order to buy 100 units of Google stock. The final task is to actually place that order via the
placeOrder method of the
Connection object. We then disconnect from TWS:
# ib_api_demo.py if __name__ == "__main__": # Connect to the Trader Workstation (TWS) running on the # usual port of 7496, with a clientId of 100 # (The clientId is chosen by us and we will need # separate IDs for both the execution connection and # market data connection) tws_conn = Connection.create(port=7496, clientId=100) tws_conn.connect() # Assign the error handling function defined above # to the TWS connection tws_conn.register(error_handler, 'Error') # Assign all of the server reply messages to the # reply_handler function defined above tws_conn.registerAll(reply_handler) # Create an order ID which is 'global' for this session. This # will need incrementing once new orders are submitted. order_id = 1 # Create a contract in GOOG stock via SMART order routing goog_contract = create_contract('GOOG', 'STK', 'SMART', 'SMART', 'USD') # Go long 100 shares of Google goog_order = create_order('MKT', 100, 'BUY') # Use the connection to the send the order to IB tws_conn.placeOrder(order_id, goog_contract, goog_order) # Disconnect from TWS tws_conn.disconnect()
The final step is to run the code:
Immediately it can be seen that the API tab opens up in Trader Workstation, showing the market order to go long 100 shares of Google:
TWS API Tab view after Google order
If we now look at the Portfolio tab we can see the Google position. You will also note a forex position in the list, which was not generated by myself! I can only assume that either the IB demo account is "shared" in some fashion (due to the identical login information) or IB places arbitrary orders into the account to make it appear more "realistic". If anybody has any insight into this behaviour I would be intrigued to learn more:
TWS API Portfolio view subsequent to Google order
This is the most basic form of automated execution that we could consider. In subsequent articles we are going to construct a more robust event-driven architecture that can handle realistic trading strategies.