Interactive Brokers Python API (Native) – A Step-by-step Guide

22 min read

Interactive Brokers (IB) is a trading brokerage used by professional traders and small funds.

If you want to learn how to build automated trading strategies on a platform used by serious traders, this is the guide for you.

Source

Table of Content

  1. What is the Interactive Brokers Python native API?
  2. Why should I learn the IB Python Native API?
  3. Why shouldn’t I learn the IB Python Native API?
  4. IB Python native API vs Third Party Libraries (IBridgePy, IbPy etc)
  5. How to set up the IB native Python API on Windows?
  6. How to retrieve the current ask price of Apple’s Stock (AAPL)
  7. Retrieving market data for other assets – EUR/USD, Bitcoin & Gold
  8. How to retrieve the last 10 hourly candlebars using the native Python API?
  9. What’s the best way to store historical data for later use?
  10. 3 ways to calculate the 20 SMA
  11. How to fire an order using the native Python API?
  12. How to implement a stop loss or take profit?
  13. How to send notifications via telegram?
  14. Download the full codebase

What is the Interactive Brokers Python native API?

The Interactive Brokers Python native API is a functionality that allows you to trade automatically via Python code.

In more technical terms, it is a communication protocol that allows for an interchange of information with Interactive Broker’s (IB) servers and custom software applications. Acting as a bridge, the API allows for sending of orders from custom software or scripts, receiving live or historical data, and several other useful applications.

TWS Chart TSLA Option
An example of a chart on the IB platform. It shows a Tesla option rising 30,000% and crashing within days – Circa 2020

Why should I learn the IB Python Native API?

Chances are that if you’re reading this guide, you’ve already done your research and concluded that Interactive Brokers (IB) has great online reviews. The broker is well-known for competitive commission rates and breadth of markets.

Learning to use the Python native API allows you to take things one step further. Here are some of the things you can accomplish:

  • Automate trading – Whether you’re seeking a fully or semi-automated solution, the API is a base point for connecting your automation scripts with Interactive brokers
  • Create a custom trading terminal – Interactive Broker’s TWS is great and packed with a ton of functionality. But if you’re looking for an alternate solution to place trades, a custom terminal can easily be built using the API.
  • Collect historical data – Having access to past data is the starting point for most automated trading systems. IB offers streaming data and is generous with its API rate limits.
  • Easily create custom indicators – TWS has standard built-in technical indicators that are widely used. However, if you’re looking to customize your own indicators, the API is the way to go. Further, Python is known for its vast libraries. If you’re interested in machine learning or sentiment analysis for example, the API offers a bridge to connect to amazing libraries available in Python for these areas.
  • Custom alerts and notifications – Do you have a need for an alert that TWS can’t fulfill? There’s a good chance you can do it Python.

Why shouldn’t I learn the IB Python Native API?

In some cases, there are easier ways to accomplish your goals. Here are a few reasons why IB’s API might not be the right fit:

  • Backtesting – There isn’t a built-in solution for backtesting strategies. There are some great third-party solutions and most even supply the data which simplifies things quite a bit. Check out the following article for more information – Backtesting Systematic Trading Strategies in Python: Considerations and Open Source Frameworks.
  • Ease of use – This detailed guide will attempt to walk you through setting up the IB API step by step. But some steps might seem a bit complicated and if you’re focused on the currency markets or only trading CFD’s, it might be worth checking Metatrader 4 or Metatrader 5. It doesn’t have as large of a learning curve and has several solutions built-in. There is even a third-party open source bridge available if you’d like to use Python with Metatrader.
  • Commissions – The costs of commissions and data fee’s add up, especially for those executing a lot of trades. While IB is known to offer low commissions, this is not the case across all markets. They also charge for data and don’t pay out interest under a certain threshold. It’s worth comparing the cost-effectiveness of trading with IB versus some of the other brokers out there before investing your time into learning the API.

IB Python native API vs Third Party Libraries (IBridgePy, IbPy etc)

The IB Python native API is officially developed and maintained by Interactive Brokers. This ensures that it will provide the most stable and error-free connection to the IB servers.

On the other hand, code wrappers and libraries like IBridgePy or IbPy are developed by third-parties and are not officially supported by IB.

How to set up the IB native Python API on Windows?

There are four basic steps to setting up a connection to the IB API in Python.

  1. Open an account with IB – IB offers demo accounts which are great for testing. If you decide to connect to a live account, there is a read-only option for the API in TWS which is useful when testing and in the early stages of getting to know the API.
  2. Download the IB Python native API – These are script files written in Python that facilitate the connection and communication with IB’s client which is in turn connected to their server.
  3. Download your IB client (TWS or IB Gateway) –  You might already be familiar with TWS, the default trading client provided by Interactive Brokers. An alternate solution is to use the Interactive Brokers Gateway client. When starting out, it’s a good idea to use TWS while testing your script as it provides a visual confirmation of any activity in your account. It’s simple to switch to the Gateway later on.
  4. Choose your IDE – We code our Python scripts on a IDE of our choice. However, since we require a constant open connection, not all IDEs are suitable.
  5. Test for connectivity – Check out code sample below

Open an account with IB

We have dedicated a separate blog post on how to do this: “How to Sign Up for an Interactive Brokers Paper Trading Account

Download the IB Python native API

You can download the Python Native API by navigating to the Interactive Brokers website and by going to Technology Trading APIsGet API Software, or by following this link – http://interactivebrokers.github.io/

Make sure to select API version 9.73 or higher as anything prior to that does not have the Python source files needed. Also, you should be using Python version 3.1 or higher.

Run the downloaded msi file and go through the setup wizard. This will copy the required Python source files to your hard drive. Once completed, navigate over to the directory that you specified in the installer and drill down to this directory – /TWS API/source/pythonclient. In this folder, run the setup.py file to install the API as a package.

Congratulations! You’ve now installed the IB API. Just to make sure it is installed correctly, go into your Python terminal and type in import ibapi. If no errors appear, the install was successful.

The IB API installer will install a few files that enable compatibility with Excel and also make a registry change in the process. If you’re looking to avoid that, check out the instructions for setting up the API in Linux or on a Mac, the method works just as well for Windows.

Install the IB API in Linux or a Mac

The process is similar to the install described above for Windows. Navigate over to the install page linked above and a ZIP file is available for download under the Mac / Linux column. Unzip the file, and navigate over to IBJts/source/pythonclient to run the setup.py file.

IB has written step by step instructions which can be found here – https://ibkb.interactivebrokers.com/article/2484

Final thoughts about installing the IB API

If you choose not to install the IB API Python source as a package, simply place your scripts in the pythonclient folder and run them from there

If you’d like to install the IB API Python package in a virtual environment, check out the following link for more details – https://packaging.python.org/tutorials/installing-packages/

Download your IB client (TWS or IB Gateway)

The Native Python API communicates to the IB servers via client software offered by the broker. There are two choices, IB Trader Work Station (TWS) and IB Gateway.

What is TWS?

TWS is the standard client that manual traders use. This client is great when you’re just starting out as it provides visual confirmation of the many commands you can send to IB via Python.

What is IB Gateway

The IB Gateway is a minimal solution that simply allows a connection to be established and requires no configuration out of the box. It’s a great solution if you’re looking to save on resources and it’s the client typically used in application developments.

If you decide to use TWS, navigate over to Trader Workstation Configuration which can be found within the TWS terminal under Edit Global ConfigurationAPI Settings. You should be looking at a screen that looks like this:

Make sure to check off Enable ActiveX and Socket Clients, this is the main requirement for the API.

If you’d like to play it on the safe side, check off Read-Only API to ensure orders don’t get executed accidentally while testing out the API.

Make note of the default Socket port, or optionally change it to another available port if you desire to do so.

IB API socket ports

Lastly, make sure Allow connections from localhost only is checked for security purposes.

The IB gateway is ready to go out of the box so there’s no need to check off the box to enable a connection like in TWS. If you’d like to configure some of the other options described above, go to the configuration page in Gateway by navigating to Configure Settings API Settings.

Choose your IDE

Simply put, an IDE (Integrated development environment) is the software that you code in.

The method used to connect to the IB servers is a rather unique one. There are two common approaches when it comes to communication with trading servers.

The first one involves a direct connection to a server. In such a scenario, a Python script can be coded in your favorite IDE and a connection is made to a server. This is typically done via the requests library or through a websocket.

The second common method is via an IDE provided by the broker which often involves coding in a language proprietary to the broker. TD Ameritrade uses this method. They provide an IDE and code is written in thinkScript which is a proprietary language to TD. Another example is Metatrader, which uses MetaQuotes Language (MQL), and also offers a built-in IDE.

The advantage that IB brings with its API is support for multiple languages and the option to code in your favorite IDE. Supported languages currently include Python, Java, C++, and .NET. There is also support for Microsoft’s ActiveX framework as well as DDE to establish a connection within Excel.

IB-Python-API-Flow-Chart
Establishing a connection to Interactive Brokers’ server

What makes IB unique is that a connection is made to the IB client software which acts as an intermediary to the IB servers. It requires an open, and constant connection which is why we use threading in the examples provided.

This presents a challenge to those that prefer to use an interactive Python development environment such as Jupyter notebooks or Spyder. The EClient functions (outgoing calls) tend to work fine but EWrapper functions (incoming data) present issues due to the lack of an open connection.

IB-insync is a third-party library that utilizes the asyncio library to provide an asynchronous single thread to interact with the API. This might be a solution to explore for those looking to use an interactive environment.

Popular Python IDE’s include IDLE, which is pre-packaged with Python, and PyCharm. VS Code, Sublime Text, and Atom also work great with Python and can be used with other programming languages as well.

If you don’t already have a favorite IDE, Sublime Text is a good option as it offers features such as code completion and syntax highlighting. It’s also easy to customize, compatible with other programming languages, and there are a ton of third-party libraries available to extend functionality.

VS code is also a good option. It offers the same functionality as Sublime Text with the added benefit of embedded Git control. Choosing an IDE comes down to personal preference and there isn’t a clear leader within the Python community when it comes to IDE’s. For this reason it’s worth testing out some of the popular ones to see which one suits your needs best.

Test for connectivity

Here is a simple code snippet to test a connection to the IB API. Make sure you change the socket port number in the function app.connect if needed. The number beside the socket port is a client id used to identify your script to the API. It can be any unique positive integer.

from ibapi.client import EClient
from ibapi.wrapper import EWrapper  

class IBapi(EWrapper, EClient):
     def init(self):
         EClient.init(self, self) 

app = IBapi()
app.connect('127.0.0.1', 7497, 123)
app.run()

Your output should look something like this:

Didn’t get an output? If you’ve tried running the script a few times and you’re not getting an output, change the client id to something unique. Another reason you might not be seeing an output could be because the script ended before a connection was established. In this case, try using a sleep timer at the end of the code snippet to pause the script for a few seconds.

The Importance of EClient and EWrapper

There are several source code files in the IB Python API client folder. The two most important files are EClient and EWrapper.

For the most part, the EClient handles all outgoing requests while the EWrapper handles incoming messages. True to its name, EWrapper acts like a wrapper for incoming messages and in most cases, a function from it will need to be overwritten in your script to redirect the output to where you want it to go.

It’s worthwhile going through some of the source code files to become familiar with the API. And remember, you can always type in help(EClient) or help(EWrapper) in your Python terminal to get more information about the functions contained within them.

All the examples provided here start from the basic script. In it, the EClient and Ewrapper are first imported. A class is then created and both these scripts are passed through into it. At this point, we instantiate the class using the app variable in our examples, and call the app.connect() command to specify the parameters required to create a connection. The app.run() command executes everything while app.disconnect() is used at the end of the script to end the session and close the connection.

We will be adding threading to the basic script. The API connection will run in its own thread to ensure that communication to and from the server is not being blocked by other commands in the main script.

How to retrieve the current ask price of Apple’s Stock (AAPL)

To get the latest ask price of a stock, we create a contract object defining the stock’s parameters and overwrite a function in the EWrapper to have the response printed to the screen.

Alternatively, you can save the response to a file or a variable. In a production environment, you’ll likely save it to a variable.

We also make a call to reqMktData which is a function within the EClient.

Here is the code:

 from ibapi.client import EClient
 from ibapi.wrapper import EWrapper
 import threading
 import time

 from ibapi.contract import Contract

 class IBapi(EWrapper, EClient):
     def init(self):
         EClient.init(self, self)
     def tickPrice(self, reqId, tickType, price, attrib):
         if tickType == 2 and reqId == 1:
             print('The current ask price is: ', price)

 def run_loop():
     app.run()

 app = IBapi()
 app.connect('127.0.0.1', 7497, 123)

 #Start the socket in a thread
 api_thread = threading.Thread(target=run_loop, daemon=True)
 api_thread.start()

 time.sleep(1) #Sleep interval to allow time for connection to server

 #Create contract object
 apple_contract = Contract()
 apple_contract.symbol = 'AAPL'
 apple_contract.secType = 'STK'
 apple_contract.exchange = 'SMART'
 apple_contract.currency = 'USD'

 #Request Market Data
 app.reqMktData(1, apple_contract, '', False, False, [])

 time.sleep(10)
 app.disconnect()

This code will make a call to request a price data stream for AAPL and print the latest price on the screen as it is updated. Let’s take a look at the parameters required for reqMktData

The ReqId is a unique positive integer you assign to your request which will be included in the response. This way, if you make several market data requests at the same time, you’ll know which returned data belongs to which asset.

The tickType, left empty in this example, allows you to specify what kind of data you’re looking for. Since the ask price is part of the default dataset returned, we don’t need to specify a tickType. You can run the code snippet below to get a full list of all the tickTypes available.

from ibapi.ticktype import TickTypeEnum

for i in range(91):
     print(TickTypeEnum.to_str(i), i)

The numerical value for the ask price is 2, hence the if statement in the tickPrice function in our script to filter out only the ask price.

The fourth parameter under reqMktData is if you want snapshot data for an asset that you do not have a subscription to. If you have a market data subscription, or one is not required, set this to False.

The fifth item is to obtain a snapshot rather than streaming data. This is for assets you already have a subscription for, or if a subscription is not required.

Retrieving market data for other assets – EUR/USD, Bitcoin & Gold

If you’d like to pull the latest ask price for other markets, simply change the contract object as necessary. The rest of the script remains unchanged.

To get the details required for the contract object, simply right click on the asset you need data for in your TWS watchlist and select description. A pop-up box will appear which contains the information you need. It looks something like this:

Now that we have the data required for EUR/USD, let’s modify the contract object.

eurusd_contract = Contract()
eurusd_contract.symbol = 'EUR'
eurusd_contract.secType = 'CASH'
eurusd_contract.exchange = 'IDEALPRO'
eurusd_contract.currency = 'USD'

And there you have it. Simply swap the contract object in your market data request to get data for the asset you need.

Interested in trading Bitcoin Futures? Here is an example of a contract object to receive market data:

BTC_futures__contract = Contract()
BTC_futures__contract.symbol = 'BRR'
BTC_futures__contract.secType = 'FUT'
BTC_futures__contract.exchange = 'CMECRYPTO'
BTC_futures__contract.lastTradeDateOrContractMonth  = '202003'

There are a few changes in the above code snippet. First, the contract currency is typically not required for a futures contract. Second, the contract expiry will need to be added.

» If you are keen on futures trading, check out our “5 Futures Trading Strategies Guide“.

And lastly, if you’re a commodities trader, check out how to get the latest price of gold:

XAUUSD_contract = Contract() 
XAUUSD_contract.symbol = 'XAUUSD' 
XAUUSD_contract.secType = 'CMDTY' 
XAUUSD_contract.exchange = 'SMART' 
XAUUSD_contract.currency = 'USD'

Tip: If you find yourself making a lot of requests for instruments within the same asset class, it might easier to create a function that will create a contract object based on pre-defined parameters.

How to retrieve the last 10 hourly candlebars using the IB Python native API?

Obtaining historical data is very similar to retrieving the latest ask price. The difference is that reqHistoricalData is called rather than reqMktData. We overwrite historicalData to handle the response. Make sure to pass in the bar object which contains all of the data.

 from ibapi.client import EClient
 from ibapi.wrapper import EWrapper
 from ibapi.contract import Contract
 import threading
 import time
 
 class IBapi(EWrapper, EClient):
     def init(self):
         EClient.init(self, self)
     def historicalData(self, reqId, bar):
         print(f'Time: {bar.date} Close: {bar.close}')

 def run_loop():
     app.run()

 app = IBapi()
 app.connect('127.0.0.1', 7497, 123)

 #Start the socket in a thread
 api_thread = threading.Thread(target=run_loop, daemon=True)
 api_thread.start()

 time.sleep(1) #Sleep interval to allow time for connection to server

 #Create contract object
 eurusd_contract = Contract()
 eurusd_contract.symbol = 'EUR'
 eurusd_contract.secType = 'CASH'
 eurusd_contract.exchange = 'IDEALPRO'
 eurusd_contract.currency = 'USD'

 #Historical Candlestick data request
 app.reqHistoricalData(1, eurusd_contract, '', '2 D', '1 hour', 'BID', 0, 2, False, [])

 time.sleep(10)
 app.disconnect()

reqHistoricalData requires a few more parameters, here is a breakdown.

Since we are looking for the 10 most recent candles, we can leave the End Date blank.

For the Interval, we selected ‘2 D’ which stands for two days. The interval is calculated from the prior day’s close so if you chose ‘1 D’ , depending on the time of day, you might get less than 10 candles.

Time Period is straightforward and we set this to ‘1 hour’ as we are looking for hourly candles.

The Data Type will typically be either BID, ASK, or MIDPOINT. On most charting platforms, the BID price is used.

For a complete list of available Data Types, Time Period’s, and Interval’s, check out – https://interactivebrokers.github.io/tws-api/historical_bars.html

RTH stands for Regular Trading Hours. It’s mostly used for stocks and if you’re looking for pre-market data, set this to 1.

There are two options for the Time Format. Set it to 1 if you want the response data to contain readable time and set it to 2 for Epcoh (Unix) time. The second option makes it much easier to convert to a Python DateTime object.

Lastly, if Streaming is set to True, it will keep updating price bars every five seconds (even if the candle has not closed).

Note: Since the last candle sent over by IB has likely not closed, it is a good idea to verify whether it has or not, and discard the last candle if needed to ensure accurate data.

What’s the best way to store historical data for later use?

An easy way to store data is by saving it as a CSV file. This can either be done using the standard write to file method in Python, or by using a built-in method in the Pandas Library.

The Pandas library was designed by traders, to be used for trading. Initially, at least, it was later modified to accompany a lot more functionality. This library allows for easy data manipulation as well as storage.

 from ibapi.client import EClient
 from ibapi.wrapper import EWrapper
 from ibapi.contract import Contract
 import threading
 import time

 class IBapi(EWrapper, EClient):
     def init(self):
         EClient.init(self, self)
     def historicalData(self, reqId, bar):
         print(f'Time: {bar.date} Close: {bar.close}')
         app.data.append([bar.date, bar.close])

 def run_loop():
     app.run()

 app = IBapi()
 app.connect('127.0.0.1', 7497, 123)

 #Start the socket in a thread
 api_thread = threading.Thread(target=run_loop, daemon=True)
 api_thread.start()

 time.sleep(1) #Sleep interval to allow time for connection to server

 #Create contract object
 eurusd_contract = Contract()
 eurusd_contract.symbol = 'EUR'
 eurusd_contract.secType = 'CASH'
 eurusd_contract.exchange = 'IDEALPRO'
 eurusd_contract.currency = 'USD'

 app.data = [] #Create empty variable to store candles

 #Request historical candles
 app.reqHistoricalData(1, eurusd_contract, '', '2 D', '1 hour', 'MIDPOINT', 0, 2, False, [])

 time.sleep(5) #sleep to allow enough time for data to be returned

 #Working with Pandas DataFrames
 import pandas

 df = pandas.DataFrame(app.data, columns=['DateTime', 'Close'])
 df['DateTime'] = pandas.to_datetime(df['DateTime'],unit='s') 
 df.to_csv('EURUSD_Hourly.csv')  
 print(df)

 app.disconnect()

In the above code snippet, we create an empty variable called app.data and direct the historicalData function to append candlestick data to it as it comes in.

To export the data using Pandas, we first create a dataframe. The pandas.to_datetime function is called to convert the incoming data to a DateTime object so that it will be easier to manipulate later on. The .to_csv is an easy way to save the data to a file. To retrieve it later on, simply call the file by saving pandas.read_csv(filename) to a variable.

3 ways to calculate the 20 SMA

There are several ways to calculate the value of the 20-period simple moving average, we will discuss three. Using pandas, a manual calculation, and utilizing a third-party library. The beauty of doing this in Pandas is that it can be achieved in just one line.

df['20SMA'] = df['Close'].rolling(20).mean()
print(df.tail(10))

That’s all it takes. Your output should look something like this:

Alternatively, if you’d like to manually calculate a moving average, use the following code snippet:

total = 0
for i in app.data[-20:]:  
    total += float(i[1])

print('20SMA =', round(total/20, 5))    

The above code totals the last 20 candle closes and divides it by 20 to derive at the 20 SMA.

The last method involves using a third-party library called TA-Lib. Several brokers use this library in their custom charting software and it is quite popular. While the original library is not available in Python, a wrapper is available to allow Python users access.

How to fire a trade using the IB Python native API?

To fire an order, we simply create a contract object with the asset details and an order object with the order details. Then call app.placeOrder to submit the order.

The IB API requires an order id associated with all orders and it needs to be a unique positive integer. It also needs to be larger than the last order id used. Fortunately, there is a built in function which will tell you the next available order id.

We can also use this built in function to confirm a connection as this order id gets sent out as soon as a connection is made.

For this reason, we’ve enabled some error checking that tells the script to wait for an order id early on in our script to ensure we are in fact connected.

 from ibapi.client import EClient
 from ibapi.wrapper import EWrapper
 from ibapi.contract import Contract
 from ibapi.order import *
 from ibapi.utils import iswrapper
 import threading
 import time

 class IBapi(EWrapper, EClient):
     def init(self):
         EClient.init(self, self)

     def historicalData(self, reqId, bar):
         print(f'Time: {bar.date} Close: {bar.close}')
         app.data.append([bar.date, bar.close])

     @iswrapper 
     def nextValidId(self, orderId: int):     
         super().nextValidId(orderId)
         self.nextorderId = orderId
         print('The next valid order id is: ', self.nextorderId)

     def orderStatus(self, orderId, status, filled, remaining, avgFullPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice):
         print('orderStatus - orderid:', orderId, 'status:', status, 'filled', filled, 'remaining', remaining, 'lastFillPrice', lastFillPrice) 

     def openOrder(self, orderId, contract, order, orderState):
         print('openOrder id:', orderId, contract.symbol, contract.secType, '@', contract.exchange, ':', order.action, order.orderType, order.totalQuantity, orderState.status)

     def execDetails(self, reqId, contract, execution):
         print('Order Executed: ', reqId, contract.symbol, contract.secType, contract.currency, execution.execId, execution.orderId, execution.shares, execution.lastLiquidity)


 def run_loop():
     app.run()
 
 #Function to create FX Order contract
 def FX_order(symbol):
     contract = Contract()
     contract.symbol = symbol[:3]
     contract.secType = 'CASH'
     contract.exchange = 'IDEALPRO'
     contract.currency = symbol[3:]
     return contract
 
 app = IBapi()
 app.connect('127.0.0.1', 7497, 123)

 app.nextorderId = None

 #Start the socket in a thread
 api_thread = threading.Thread(target=run_loop, daemon=True)
 api_thread.start()

 #Check if the API is connected via orderid
 while True:
     if isinstance(app.nextorderId, int):
         print('connected')
         break
     else:
         print('waiting for connection')
         time.sleep(1)

 #Create order object
 order = Order()
 order.action = 'BUY'
 order.totalQuantity = 1000
 order.orderType = 'LMT'
 order.lmtPrice = '1.10'

 #place order
 app.placeOrder(app.nextorderId, FX_order('EURUSD'), order)
 #app.nextorderId += 1

 time.sleep(3)

 #cancel order
 print('cancelling order')
 app.cancelOrder(app.nextorderId)

 time.sleep(3)
 app.disconnect()

In order to confirm that a connection is established, we are waiting for the API to send over the nextorderid and holding the script in a loop with a sleep timer until it is received. This is to confirm that a connection has been established.

In addition to that, we’ve also created a function to create a contract specific to Forex. This simplifies contract creation as most of the parameters are similar.

To place an order, we create an order object which specifies whether you’re looking to buy or sell. The order size and limit price are also set here. If you’d like to create a market order, set order.orderType to ‘MKT’ and comment out the orderlmtPrice.

Remember to increment your nextorderId after placing an order.

You’ll also notice several additional functions defined near the top of the script. These are all the messages returned by EWrapper associated with placing orders. Similar to before, you might want to save some of these to variables for later use.

Here is what your output should look like after running the above script:

The API treats many items as errors even though they are not. For example, the order cancellation came up as an error even though there were no issues. This can be changed by overriding the EWrapper function for error messages. Here is an example:

def error(self, reqId, errorCode, errorString):
    if errorCode == 202:
       print('order canceled')  

A complete list of API codes can be found here – https://interactivebrokers.github.io/tws-api/message_codes.html

It is a good idea to use the codes associated with market data connections to ensure you have an active data connection and implement error checking when submitting orders to ensure the connection is active and price data is fresh.

How to implement a stop loss or take profit using the IB Python native API?

A stop loss is essentially an order to execute once a certain price is reached. It’s a good idea to ‘group’ stop loss orders with your original order. This way, if you decide to delete your original order, your stop order gets deleted automatically.

IB refers to the grouping of orders as a bracket order. The main order is considered the ‘parent’ and the stop loss, or take profit, is considered a ‘child’ order.

The two orders are tied together by assigning the order number of the parent order as a parentId in the child order. Although these two orders form one bracket order, note that a separate orderId is required for both orders so remember to increment and assign an orderId to your stop loss or take profit orders.

Another important thing to keep in mind is that the parent order has the line order.transmit = False. This is to ensure the first order does not get processed until the rest of the bracket orders are transmitted. The last order sent via placeOrder should have order.transmit = True to process the entire bracket order.

 from ibapi.client import EClient
 from ibapi.wrapper import EWrapper
 from ibapi.contract import Contract
 from ibapi.order import *
 from ibapi.utils import iswrapper
 import threading
 import time

 class IBapi(EWrapper, EClient):
     def init(self):
         EClient.init(self, self)

     def historicalData(self, reqId, bar):
         print(f'Time: {bar.date} Close: {bar.close}')
         app.data.append([bar.date, bar.close])

     @iswrapper 
     def nextValidId(self, orderId: int):     
         super().nextValidId(orderId)
         self.nextorderId = orderId
         print('The next valid order id is: ', self.nextorderId)

     def orderStatus(self, orderId, status, filled, remaining, avgFullPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice):
         print('orderStatus - orderid:', orderId, 'status:', status, 'filled', filled, 'remaining', remaining, 'lastFillPrice', lastFillPrice) 

     def openOrder(self, orderId, contract, order, orderState):
         print('openOrder id:', orderId, contract.symbol, contract.secType, '@', contract.exchange, ':', order.action, order.orderType, order.totalQuantity, orderState.status)

     def execDetails(self, reqId, contract, execution):
         print('Order Executed: ', reqId, contract.symbol, contract.secType, contract.currency, execution.execId, execution.orderId, execution.shares, execution.lastLiquidity)


 def run_loop():
     app.run()
 
 #Function to create FX Order contract
 def FX_order(symbol):
     contract = Contract()
     contract.symbol = symbol[:3]
     contract.secType = 'CASH'
     contract.exchange = 'IDEALPRO'
     contract.currency = symbol[3:]
     return contract
 
 app = IBapi()
 app.connect('127.0.0.1', 7497, 123)

 app.nextorderId = None

 #Start the socket in a thread
 api_thread = threading.Thread(target=run_loop, daemon=True)
 api_thread.start()

 #Check if the API is connected via orderid
 while True:
     if isinstance(app.nextorderId, int):
         print('connected')
         break
     else:
         print('waiting for connection')
         time.sleep(1)

 #Create order object
 order = Order()
 order.action = 'BUY'
 order.totalQuantity = 1000
 order.orderType = 'LMT'
 order.lmtPrice = '1.10'
 order.orderId = app.nextorderId
 app.nextorderId += 1
 order.transmit = False
 
 #Create stop loss order object
 stop_order = Order()
 stop_order.action = 'SELL'
 stop_order.totalQuantity = 1000
 stop_order.orderType = 'STP'
 stop_order.auxPrice = '1.09'
 stop_order.orderId = app.nextorderId
 app.nextorderId += 1
 stop_order.parentId = order.orderId
 order.transmit = True
 
 #place orders
 app.placeOrder(order.orderId, FX_order('EURUSD'), order)
 app.placeOrder(stop_order.orderId, FX_order('EURUSD'), stop_order)

 app.disconnect() 

A take profit can be added by creating an Order() object similar to the above. The variable for price in a take profit might look something like this take_profit.lmtPrice since the take profit is a limit order. Remember, whichever order is sent last should have the transmit=True while the rest should have transmit=False.

How to send notifications via telegram and the IB Python native API?

Now that you’re able to get market data and create orders, you might want to implement some kind of an alert system. Perhaps when an order gets triggered, or a certain price point is reached.

Telegram allows for an easy way to create a live alert and it is also capable of two way communication.

To setup a bot in the Telegram:

  1. Open a chat with the ‘BotFather’ from within Telegram.
  2. Type in the command /newbot
  3. It will prompt you to enter a bot name and send you a access token.
  4. Open a new chat with your newly created bot.
  5. Type something in to activate the chat.
  6. Go to the following URL – https://api.telegram.org/botxxxxxxxxxxxxxxxx/getUpdates  – replacing the XXX with your access token. Here you should see a JSON structure. Note down the id (not to be confused with update_id or message_id).

At this point, the bot is created and messages can be sent to it. Here is a code snippet to test if everything is working:

import requests
def send(text):
       token = 'xxx'
       params = {'chat_id': xxx, 'text': text, 'parse_mode': 'HTML'}
       resp = requests.post('https://api.telegram.org/bot{}/sendMessage'.format(token), params)
send('hello')    

Remember to update the script with your own access token and chat id.

You should have received a ‘hello’ message in your Telegram chat. You can now use this script to send several different types of useful messages from your Python script.

For example, you might want to get a Telegram alert every time your script fires off an order. Here is a way you might do that:

 import requests  
 def send(pair, order, stop_order):  
      text = f'A new trade has been placed in {pair} at {order.lmitPrice} with a stop at {stop_order.auxPrice}'  
      token = 'xxx'  
      params = {'chat_id': xxx, 'text': text, 'parse_mode': 'HTML'}  
      resp = requests.post('https://api.telegram.org/bot{}/sendMessage'.format(token), params)  
 
 send('EURUSD', order, stop_order)   

This provides an easy way to keep on top of any orders executed. You can also utilize the alert system in a try/except block to pick up any errors that the script might be picking up on. While logging is often used in such scenario’s, there is a higher sense of urgency in algo trading when it comes to script problems which Telegram can address.

Download the full codebase

Github link: https://github.com/Lucas170/Interactive-brokers-python-native-api-guide (Click the green button on the right “Clone or download” to download or clone the code)

The folder “ibapi” contains the default code that comes along when you install the IB API.

The “disconnect_error_fix” folder contains a code-fix for the default IB API. In our testing, we found a persistent error message when calling the disconnect() command at the end of the script. This command is necessary, without it, the IB API won’t register that the script has ended and will refuse subsequent connections with the same client id.

What’s next?

Now that you have learnt some programming. Learn some trading from our sentiment analysis or futures trading guides!

380 Views
Jignesh Davda

How to use Hedging as a Trading Strategy

How to use hedging as a trading strategy? We use it to take on specific risks. There are 2 ways. 1) Asset-hedge: This means...
Lucas Liew Lucas Liew
4 min read