The discount brokerage space is getting very competitive with commissions going to zero recently at many brokerages, including Interactive Brokers. IB has long been a broker with one of the largest breadth of products and service offerings targeting a professional audience. IB is also a low cost brokerage, so that makes it a good option amateur investors and traders. It also can be a way to learn more about algorithmic or systematic trading. In this article I will cover the basics of connecting to Interactive Brokers via Python to get the latest market data for an instrument.
APIs
IB offers a set of application programming interfaces (APIs) to their data and services, available for Java, .NET (C#), C++, Python, or DDE, using their Trader Workstation Application Programming Interface (TWS API). Their API is a bit different from some other offerings in that it is not a web-based REST API. Rather, IB uses a proprietary API that connects to a local server running on your computer which then connects to IB servers over your internet connection. You can choose to run the full Trader Workstation GUI (TWS), or a slightly smaller Gateway that is only used for API access. I’ll focus on using TWS here.
This has a few obvious drawbacks:
- You need to learn a new unique API. If you’re familiar with REST APIs, this will be a bit different.
- There is an extra piece of software that runs between your code and the market, introducing some latency.
- You have to authenticate to the API using a GUI. This means you have to have a visual display, making it a challenge to run your code on docker or a cloud server. Even though headless environments aren’t explicitly supported, there are some workarounds available.
Life isn’t all bad though, the API is fairly well documented and quite complete! The advantage of a fully built GUI is that you can try things there first and then search through the API to find the corresponding calls needed to perform the transactions. It’s a good way to slowly build up and automate a trade.
Here are the basic steps to get up and running with Interactive Brokers. The example I’ll use is for an individual, non-professional regular account. You could also open an Individual Retirement Account (IRA).
Setup account
First, from the main IB web page, follow the instructions to open an account. You’ll need to fill out a pretty standard brokerage application and then fund your account. IB can change the minimum balance required. It also may vary based on your account type. Once you have an account, they will give you a unique username and you can set your password.
Download TWS software
After setting up your account on their website, I recommend downloading the phone based IB software and setting up two factor authentication available under User Settings, Secure Login System. This will require you to authenticate via your phone whenever authenticating to the IB servers or web site.
Next, download the current version of the TWS software (this was 10.16.1h at time of writing). This is available right off the IB home page, under Trading, then Platforms. Click on Trader Workstation, then choose the option to download the latest software, not the stable version. Follow the Interactive Brokers instructions for installing the software. This is a Java desktop application that will run on your computer, and it will update itself when you restart.
Finally, download the API software from the IB GitHub account. You will need to agree to their license terms, then download the stable version of TWS for your platform. The software package will include the C++, C#, and Python APIs and example code.
Subscribe to market data
Since this example will show you “real time” ticks, you will need to subscribe to market data. IB is willing to show you delayed data (15 minute delayed for US Stocks, for example) and historical data in the TWS GUI, but if you want to access data using the API, you will almost certainly require a market data subscription. For the purposes of this tutorial, you could choose to subscribe to only one exchange for US stocks, such as NASDAQ ($1.50/month at time of writing). IB also offers bundles if you would like to access a wider universe of products.
Configure TWS for API access
There is plenty of detail on how to configure TWS in the IB docs, but I’ll summarize the main points here. Once you have TWS running, you need to configure API access in the settings. Using the GUI, under File, Global Configuration, API, Settings. You will need to check the “Enable ActiveX and Socket Clients” at a minimum, and take note of the Socket Port in the configuration since we’ll need that later. Also ensure the socket is not blocked by your local firewall software. You can add “127.0.0.1” to the trusted IPs list if you don’t want to have to approve the API connection manually. For safety, you should not open this socket to the world, keep the “Allow connections from localhost only” box checked.
Setup the python environment
I recommend using pyenv for creating a virtual environment and isolating the Python version you use for development. Note that IB’s API requires Python 3, so make sure you use a modern version. For this example, I used Python 3.10.4. If you have another method you prefer, go ahead and setup the environment to install the IB API.
> pyenv install 3.10.4 > pyenv virtualenv 3.10.4 ib-example > pyenv activate ib-example # also install wheel for build step in the following section > pip install wheel
Now navigate to the Python directory where you placed the IB API download from earlier and build and install the package (I placed the unzipped archive in my projects directory). Note that the README.md file distributed with the code has more complete instructions but may not have the correct version listed in the commands, so you may need to peek in the dist directory after building to see the wheel file name. I also did the pip install without the –user option since I want it installed in my virtualenv.
> cd ~/projects/twsapi_macunix/IBJts/source/pythonclient/ > python setup.py bdist_wheel [lots of output] # note the version may be different for you with later downloads, so just look at what it build in dist... > ls dist ibapi-9.81.1-py3-none-any.whl > python -m pip install --upgrade dist/ibapi-9.81.1-py3-none-any.whl
Write the application
I’ve created a simple command line application that demonstrates how to use the IB APIs. Most of this is taken from the example IBJts/samples/Python/Testbed/Program.py
that is distributed with the IB source code. Any program that interacts with the TWS server needs to implement two interfaces. The first is EWrapper
that implements all the callbacks to handle any data messages sent to your client. The second is EClient
, which allows you to connect and send messages to TWS. I implement both of these in one class.
class MarketDataApp(EClient, wrapper.EWrapper): def __init__(self, contracts: ContractList, args: argparse.Namespace): EClient.__init__(self, wrapper=self) wrapper.EWrapper.__init__(self)
Handlers
This class also needs to implement a few methods in order to establish a connection and begin processing messages. The documentation states that the API is not ready until the client has received the next valid order number in the nextValidId
callback. You receive messages in methods that are decorated with the @iswrapper
decorator.
@iswrapper def connectAck(self): logging.info("Connected") @iswrapper def nextValidId(self, orderId: int): logging.info(f"Next valid order id: {orderId}") self.start()
Start method
You also implement a start
method in the class that you invoke once you know the API is ready. In that method you can invoke any of the IB APIs. In my case I will make requests for market data for a list of Contracts that were provided on the command line.
def start(self): if self.started: return self.started = True for contract in self.contracts: rid = self.next_request_id() self.reqMktData(rid, contract, "233", self.args.snapshot, False, []) self.pending_ends.add(rid) self.request_contracts[rid] = contract
The main method
Now, in your main method, create an instance of your class and invoke the run
method to get things started.
app = MarketDataApp(contracts, args) app.connect("127.0.0.1", args.port, clientId=0) app.run()
I’ve uploaded the full example as a Gist on GitHub. You should be able to complete the above steps and run the application in your virtualenv and see tick data in your console. The application is setup with INFO logging enabled. You will see logging of all of the API requests being made by the app. You also will see some messages logged at ERROR that indicate the status of the IB servers. And if everything worked correctly, you will see individual messages for each market update. To exit, just hit Ctrl-C a few times to break the event loop.
> python get_ticks.py AAPL INFO:ibapi.client:sent startApi INFO:ibapi.client:REQUEST startApi {} <messages omitted> ERROR:ibapi.wrapper:ERROR -1 2104 Market data farm connection is OK:usfarm.nj <messages omitted> 1 LAST price: 317.95 CanAutoExecute: 0, PastLimit: 0, PreOpen: 0 1 LAST_SIZE size: 4 1 BID_SIZE size: 1 1 ASK_SIZE size: 2 1 LAST_SIZE size: 4
One other item of note, the streaming market data requested here is not a tick-by-tick data stream, but a snapshot posted every 250ms. This delay differs by product type. To get tick-by-tick data, you’d use the reqTickByTickData
API.
From here, you can build anything you want that is available in the IB APIs. Look at the IB example code for more inspiration.
You can also read the next article to learn how to download historical data from IB.
I get this error when I run the code:
TypeError: MarketDataApp.error() takes 4 positional arguments but 5 were given
Hi, thanks for reporting that. Can you open an issue in GitHub and give the API version you’re using? It sounds to me like you may be on an older version.
This might help: https://stackoverflow.com/questions/74412482/ibrk-tws-api-error-takes-4-positional-arguments-but-5-were-given