0%

【How 2】Set Up Trading API Template In Python - Connecting My Trading Strategies To Interactive Brokers

Building your trading strategy to connect to a broker with the broker’s proprietary API is always dreadful. There are tones of API documentation to read, tones of trial-and-error tests to conduct, and tones of unknown causes and bugs that fail your API test. In this post, I’m going to demonstrate my MVP API template to get my trading strategies to work, so that you can build your own in a way that makes your trading strategies work as well.


If you enjoy reading this and my other articles, feel free to join Medium membership program to read more about Quantitative Trading Strategy.


Previous researches

After completing the research CPPI investment strategy, I need to find a new broker with a new account to start trading. Interactive Broker (IBKR) is the first broker that I am familiar with when I was a student, so I kinda have this obsession to make my trades through it. Years later, I consider myself finally had the luxury to sit down and spend time reading their API documentation. I’ve found the IBKR API doesn’t support retrieving historical transaction data, for example, the orders you placed three days ago and the commissions you paid for each trade wouldn’t be stored anywhere in the IBKR server. However, I need this information to build my performance evaluation report. Therefore, I’ve decided to put it on hold until there’s a good enough solution to come to rescue me. Now, as my knowledge grows, I’ve figured it’s about time to tackle this task.

Interactive Broker (IBKR) is a renowned investment broker that has successfully operated its business across the world. It is also famous for its low trading fees in both the equity and the derivative markets. On the other hand, its proprietary API is notorious for being complicated enough to work with. I’m going to give you my two cents here and hopefully it’ll help people who would like to consider Interactive Broker as their market broker. Here are the things I’m going to talk about:

  • IBKR TWS and IB gateway
  • IB gateway configurations
  • My MVP broker API template
  • Introduce ib_insync package and start connecting
  • Implement our first IBKR call

IBKR TWS and IB gateway

In order to connect to the broker’s API service, each broker provides different methods to do so. TD Ameritrade API allows you to use their API service remotely through the API token provided. Futu OpenAPI requires you to download extra software on your local PC/laptop, so that your API calls will be able to access their API service through this middleware. As for [IBKR API], it is similar to Futu OpenAPI that all the API calls are connected through its proprietary software to reach the API service.

How does your API call reach the IBKR API service

Interactive broker provides two software applications to help you connect to their API service:

TWS
TWS stands for Trader Workstation. TWS is designed for trades who would like to conduct a series of research and trade equity and derivatives across many markets in one unified platform. Users can read the latest news, study company fundamentals and annual reports, research the stock trend or patterns, and even place orders with it. Also, it embeds the capability of being an intermediary between your desktop/laptop and the IBKR API service.

IBKR TWS Trading Station

IB gateway
Compared to TWS, IB gateway is simply an API gateway without all the User Interface that you can see in the TWS. You can’t buy or sell or do anything with the IB gateway. It allows you to connect to the IBKR API service and nothing more. In general, it is a super lightweight TWS that will consume much less of your desktop/laptop memories and resources.

IBKR IB Gateway

These two software applications have similar configurations but hold separate parameters which they don’t share mutually. Now let’s take a look at the configurations that will concern us before we start programming our API connection to IBKR.

IB gateway configurations

Let’s use IB gateway as an example so that we don’t get distracted by the various features that TWS offers. First of all, we need to log in to the IB gateway software application. I don’t want to mess around with my real money while testing my trading script, I would instead use a paper account. (Tip: You can reset your paper account on your account management page every day.)

Login to your paper account

Here is a few configurations that you need to pay attention to before starting to code:

  1. In API -> Setting, uncheck the “Read-Only API”.
  2. In API -> Setting, remember or configure the “Socket port” because you will need it when connecting to this software.
  3. In API -> Precautions, check the box “Bypass Order Precaution for API Order” to prevent additional errors or warning dialog boxes popped up when you place orders through API.
  4. If you’re using TWS as your middleman service, you need to check one more box “Enable ActiveX and Socket Client”.

API configuration

API configuration 2

Now we’re all set. Let’s get down to the business.

My MVP broker API template

There are a few actions required in your trading strategies in order to run the basic buy/sell operations properly:

  1. Connect to and disconnect from the dedicated broker API service.
  2. Get your basic account info in order to know the account status such as Total asset value, Remaining cash balance, Purchasing power, … and so on.
  3. Check the trading calendar and trading hours to see whether the market is open for trading or if it’s a holiday today.
  4. Check the current quote price of a specific symbol in order to know how many shares we would purchase.
  5. Place orders through the broker API.
  6. Get the transaction history for performance evaluation later on.

I use the python package abc (abbreviation for Abstract Base Class) to build my base API template. The most obvious advantage of building a baseclass with an abstract base class is that you can easily extend from it to build another class. For example, you have a class called InteractiveBrokerClass to make trades, and another class called TDAmeritradeBrokerClass to make trades with TD Ameritrade. Both broker classes do similar things and require similar functions. Implementing them using as abstract base class would make your life easier in terms of managing the actions in both derived classes. Here’s my broker base class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# TradeAPI.py
import abc

class AbstractTradeInterface(metaclass=abc.ABCMeta):
@abc.abstractmethod
def connect(self):
pass

@abc.abstractmethod
def get_account_detail(self):
pass

@abc.abstractmethod
def get_last_price_from_quote(self):
pass

@abc.abstractmethod
def place_order(self):
pass

@abc.abstractmethod
def is_market_open(self):
pass

@abc.abstractmethod
def is_market_open_now(self):
pass

@abc.abstractmethod
def get_transactions(self):
pass

Abstract Trading API Template

Implementing any of them could be straightforward and also could be extremely complex, depending on the design of the structure of the proprietary broker API. Like I said earlier that Interactive Broker didn’t support querying historic transactions/trades, we need to build alternative functions on the side in order to support the def get_transactions(self) in our base template. I’m not going to dive into how to build them now and we will come back to this in another post. I will start by looking at how we are going to connect to the IB gateway by implementing the def connect() function, and then we can check the quote price and place orders with API calls whenever we want to.

Introduce ib_insync package and start connecting

Instead of using the native ib_api package to connect to the IBKR API service, I choose to use ib_insync package developed by Ewald R. de Wit. ib_insync not only simplifies the way to connect and communicate with the IBKR API service, but it also adds the asynchronous capability so that less CPU time was wasted while requesting data from the server. Here is an introductory post to get you familiar with the functions provided in ib_insync: ib_insync: Interactive Broker API guide. We can clearly learn that we can connect to the IBKR API service with the following code:

1
2
3
4
5
6
7
ib = IB()
ib.connect(
host='127.0.0.1', # local host IP
port=4002, # The port that we configured in the IB gateway
clientId=1 # The non-duplicated client ID for each connection
)
ib.disconnect() # To disconnect from the server

You would need to connect to server before you make any request

This established connection ib was handled and maintained by whoever initiated it. In order to better and easier to handle the connection and close it effectively once we finished using it, I would suggest using contextmanager so that the context manager will close the connection once we finished using it. We don’t have to explicitly disconnect from the API service. Instead, the context manager will handle it every time when the with clause is finished.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# test.py
@contextmanager
def connect(self):
self.client = ib_insync.IB()
self.client.connect('127.0.0.1', 4002, 300)

yield self # Return the self instance

self.client.disconnect()
self.client.sleep(2) # make sure the connection is closed before next time you connect to IBKR API service

if __name__ == '__main__':
with broker.connect() as c:
# Make requests to the server
# The connections will be closed since we disconnect from the server after `yield self`

Connect function implemented with context manager

Implement our first IBKR call

In the last part of this post, let’s try to implement get_account_detail() call in our broker class so that we could learn the account status.

In the original ib_insync document, I found out that ib.managedAccounts() can retrieve a list of account names, and ib.accountValues(account:str) can retrieve all stats under this account parameter. Hence, I’m going to:

  1. First, use ib.managedAccounts() to retrieve all the accounts created.
  2. Use ib.accountValues() to get all variables related to this account.
  3. Extract the TotalCashBalance and StockMarketValue concerned USD so that I could tell how much money I have in cash and as well the total value under my account.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# test.py
from contextlib import contextmanager
import ib_insync
from zoneinfo import ZoneInfo
from modules.broker.TradeAPI import AbstractTradeInterfac

class InteractiveBrokerTradeAPI(AbstractTradeInterface):
def __init__(self,currency='USD'):
self.client = None
self.accounts = []
self.currency = currency

@contextmanager
def connect(self):
self.client = ib_insync.IB()
self.client.connect('127.0.0.1', 4002, 300)

yield self

self.client.disconnect()
self.client.sleep(2) # make sure the connection is closed before next time you connect to IBKR API service

def get_account_detail(self):
self.accounts = self.client.managedAccounts()

acc_data = []
for account in self.accounts:
acc = {}
acc['account'] = account
data = self.client.accountValues(account)
acc['cash'] = 0
acc['total_assets'] = 0
for row in data:
if row.tag in ['TotalCashBalance'] and row.currency == self.currency:
acc['cash'] = row.value
acc['total_assets'] += float(row.value)
elif row.tag in ['StockMarketValue'] and row.currency == self.currency:
acc['total_assets'] += float(row.value)
acc_data.append(acc)
return acc_data

def get_last_price_from_quote(self):
pass

def place_order(self):
pass

def is_market_open(self):
pass

def is_market_open_now(self):
pass

def get_transactions(self):
pass

# Main function
if __name__ == '__main__':
broker = InteractiveBrokerTradeAPI()
with broker.connect() as c:
accounts = c.get_account_detail()
print(accounts)

Full code

1
2
# Output
>> [{'account': 'DU4399668', 'cash': '77.44', 'total_assets': 96737.42}]

Account status output

Noted: Make sure you attach the rest of the unimplemented functions as I did, as this is required in an abstract class. Otherwise, you will see the following error message when you run the test code.

1
2
3
4
Traceback (most recent call last):
File "/Users/michael/quantitative-strategy/app/trading/test.py", line 86, in <module>
broker = InteractiveBrokerTradeAPI(version_param=SCRIPT_VERSION)
TypeError: Can't instantiate abstract class InteractiveBrokerTradeAPI with abstract methods get_last_price_from_quote, get_transactions, is_market_open, is_market_open_now, place_order

You need to implement all defined functions in the base class

Conclusion

Done! We’re not officially connecting our trading script to the Interactive Broker API service. To make sure this broker class can fully support the functionalities of your trading program, there are still more functions to be implemented. Don’t worry, I’ll see you next time.

Disclaimer: Nothing herein is financial advice or even a recommendation to trade real money. Many platforms exist for simulated trading (paper trading) which can be used for building and developing the strategies discussed. Please use common sense and consult a professional before trading or investing your hard-earned money.

Enjoy reading? Some donations would motivate me to produce more quality content