Automate your algo trading with Python and IB
Automate your algo trading with Python and IB
Automate your algo trading with Python and IB
In today’s issue, I’m going to show you how to send a trade order to Interactive Brokers (IB) with Python.
IB is a US-based broker that has an API that lets traders send trade orders through algorithms. IB has cheap commissions, access to global markets, and good execution. I recommend using IB for trading, both manually and algorithmically.
Some traders use the API to automate part of their trading. Some build sophisticated algorithms for fully-automated trading systems. Others use the API for storing real-time market data.
If you don’t have an IB set up yet, head over to Interactive Brokers and set one up. If you use this link, you can earn up to $1,000.
By the end of this issue, you’ll know how to:
- Install the Python API
- Install Trader Workstation
- Connect to IB through Python
- Send a stock order to IB through Python
Let’s go!
Step 1: Install the Python API
Download the Python API from the Interactive Brokers GitHub page.
Download the stable Windows or Mac / Unix version of the API depending on your machine.
Install on Windows
Run the msi file and go through the setup wizard. Once completed, navigate to the directory that you specified in the installer. Within the install directory, navigate to /TWS API/source/pythonclient. In this folder open a command prompt and run python3 setup.py install
to install the API.
Install on Mac or Linux
Follow the step-by-step instructions here https://ibkb.interactivebrokers.com/article/2484.
Test the installation at your Python prompt with import ibapi
. If no errors appear, the installation was successful.
Step 2: Install Trader Workstation (TWS)
TWS is IB’s trading app. It’s great to use if you want to see what happens when you send trade orders with Python.
You need to change some settings. Navigate to Trader Workstation Configuration under Edit –> Global Configuration –> API –> Settings. You should a screen that looks like this:
Make sure to check Enable ActiveX and Socket Clients. Check Read-Only API if you want extra protection against sending orders to IB. Lastly, check Allow connections from localhost only for security.
Make note of the Socket port which you’ll need to connect through Python. Depending on the version, it’s either 7497 or 7496.
Step 3: Test your connection
The Python API uses TWS to connect to the IB servers. After you log in to TWS, you can connect to IB with Python. Make sure you change the socket port number in the function app.connect
if needed. The first parameter is the IP address of your local computer (i.e. localhost). Leave this as is. The second is the port you socket port you configured in TWS. Change this if it differs from TWS. The third parameter is a unique client ID. You can change this to any positive integer.
1from ibapi.client import EClient
2from ibapi.wrapper import EWrapper
3
4class IBapi(EWrapper, EClient):
5 def __init__(self):
6 EClient.__init__(self, self)
7
8app = IBapi()
9app.connect("127.0.0.1", 7497, 123)
10app.run()
11
12# uncomment this section if unable to connect
13# import time
14# time.sleep(2)
15# app.disconnect()
After you run the code, your output should look something like this:
If you don't get output, change the client ID and try again. If that doesn’t work, uncomment the last three lines the script. If that doesn’t work, investigate based on the log output.
Step 4: Buy AAPL
To buy a stock, you send information about what you want to buy to IB through the API. The app.placeOrder
the method does all the hard work for you.
First, import the classes from the IB API. Import threading to run the app in a single thread and time to pause execution of the script.
1from ibapi.client import EClient
2from ibapi.wrapper import EWrapper
3from ibapi.contract import Contract
4from ibapi.order import *
5
6import threading
7import time
This class creates the app. The app is what you use to interact with IB.
This class inherits two classes from the IB API. If this doesn’t make sense, don’t worry. The API needs an order ID associated with every order. nextValidId
is a built-in function that finds the next available order ID. The other methods log output so you can see what's happening.
1class IBapi(EWrapper, EClient):
2 def __init__(self):
3 EClient.__init__(self, self)
4
5 def nextValidId(self, orderId: int):
6 super().nextValidId(orderId)
7 self.nextorderId = orderId
8 print("The next valid order id is: ", self.nextorderId)
9
10 def orderStatus(
11 self,
12 orderId,
13 status,
14 filled,
15 remaining,
16 avgFullPrice,
17 permId,
18 parentId,
19 lastFillPrice,
20 clientId,
21 whyHeld,
22 mktCapPrice,
23 ):
24 print(
25 "orderStatus - orderid:",
26 orderId,
27 "status:",
28 status,
29 "filled",
30 filled,
31 "remaining",
32 remaining,
33 "lastFillPrice",
34 lastFillPrice,
35 )
36
37 def openOrder(self, orderId, contract, order, orderState):
38 print(
39 "openOrder id:",
40 orderId,
41 contract.symbol,
42 contract.secType,
43 "@",
44 contract.exchange,
45 ":",
46 order.action,
47 order.orderType,
48 order.totalQuantity,
49 orderState.status,
50 )
51
52 def execDetails(self, reqId, contract, execution):
53 print(
54 "Order Executed: ",
55 reqId,
56 contract.symbol,
57 contract.secType,
58 contract.currency,
59 execution.execId,
60 execution.orderId,
61 execution.shares,
62 execution.lastLiquidity,
63 )
Next, create a function to run the app. Then, define a stock contract (IB calls everything a contract).
1def run_loop():
2 app.run()
3
4def stock_contract(
5 symbol,
6 secType='STK',
7 exchange='SMART',
8 currency='USD'
9):
10 # create a stock contract
11 contract = Contract()
12 contract.symbol = symbol
13 contract.secType = secType
14 contract.exchange = exchange
15 contract.currency = currency
16
17 return contract
Now that the setup is out of the way, make the connection and start a thread. The while loop checks if the API is connected. If it is, app.nextorderId
returns an int
. Otherwise, it returns None
.
1app = IBapi()
2app.connect('127.0.0.1', 7497, 123)
3
4app.nextorderId = None
5
6api_thread = threading.Thread(target=run_loop, daemon=True)
7api_thread.start()
8
9while True:
10 if isinstance(app.nextorderId, int):
11 print('connected')
12 break
13 else:
14 print('waiting for connection')
15 time.sleep(1)
The next step is to create an order and send it to IB. This is where the core logic of your algorithm lives. In this case, I keep it simple and just buy 10 shares of AAPL. IB expects the order to be a Python object with buy or sell, quantity, and order type. Because I send a limit order, I set the limit price too. Finally, I send the order, wait three seconds, cancel it for the sake of this demo, wait three more seconds, then disconnect the app.
1order = Order()
2order.action = "BUY"
3order.totalQuantity = 10
4order.orderType = "LMT"
5order.lmtPrice = "130.00"
6
7app.placeOrder(app.nextorderId, stock_contract("AAPL"), order)
8
9# uncomment if you are sending more than one order
10# app.nextorderId += 1
11
12time.sleep(3)
13
14# cancel the order for the demo
15print('cancelling order')
16app.cancelOrder(app.nextorderId, "")
17
18time.sleep(3)
19app.disconnect()
When I run the script, the order is sent to IB and cancelled three seconds later. This is what the output looks like.
I see my cancelled order in the Orders pane of TWS.
Algorithmic trading is not easy. On top of finding, testing, and validating an edge, you have the technology to deal with. In real trading systems, you need to worry about the logic to cancel orders, checking if orders were executed, handling bad data, checking portfolio constraints, risk management, and many other things.
When you’re just getting started, execute everything in a paper trading account. You can find instructions to set up a paper trading account here. Trade manually alongside the algorithm to make sure trades execute how you expect. This way, you can fix any issues in your script too.
When you go live, execute with a very small size. Expect to lose the money you’re trading with. The longer you do this, and experience different market conditions, the more confidence you’ll have that things will go well.
Well, that's it for today. I hope you enjoyed it.
See you again next week.