Compile IBKR (Interactive Brokers) API Client Library in C++ on macOS/Linux
I’d been working on writing algo scripts in Python for my side project and research for a while. However, I’m indeed sick of such scripting languages where I have to guess what types would be returned from the server, and obviously Python is extremely slow (though I’m not using it for HFT).
I have been working on C++ for years, so I tried to compile IBKR’s official API library. However, the compilation process is really tough. It is indeed hard to find thorough documentation that tells you how to compile their C++ library, especially when compiling version 10.37.02
which introduced protobuf. Their documentation is definitely broken. You can download the source code from here.
Note that this post is inspired by this blog. Thanks to his work, I could compile the old version library; however, it’s difficult to find the old one. I referenced that post to compile the IBKR part only.
Prerequisites
This post records how I compile IBKR’s C++ library on Linux/macOS, so you should definitely have such an environment. Most of the flow for these two platforms is similar. I’ll post the Windows version in another post.
The following is my environment:
Linux:
- Ubuntu 24.04
- gcc 13.4.0
macOS:
- macOS 15.6.1
- gcc (indeed Apple clang) 17.0.0 arm64-apple-darwin24.6.0
Download IBKR library
The official URL is https://interactivebrokers.github.io/. My suggestion is to use stable versions. In this post, I’ll use 10.37.02(Linux/macOS).
Once you’ve downloaded the zip file, decompress it somewhere, e.g. ~/Downloads/twsapi_macunix.1037.02
Building Protobuf
There are two ways to build and install protobuf: from a package manager (apt-get, homebrew) or from source code.
Install protobuf
I suggest this way because it’s much simpler and a lifesaver.
The official document (twsapi_macunix.1037.02/IBJts/source/ProtoBuf_readme.txt) mentioned that:
Protobuf C++ version 5.29.3 (Windows)
Protobuf C++ version 3.12.4 (Linux)
So I’ll show you how to install protobuf on Linux and macOS. I won’t install it from source—it’s time-consuming, and I don’t want to bother myself with such a painful process. Fortunately, both Ubuntu and macOS can install 3.21.12 with apt-get and brew.
Ubuntu:
sudo apt install libprotobuf-dev protobuf-compiler
macOS:
brew install protobuf@21
Note that, with brew, you should use protobuf@21
to specify the version as 3.21 to install the same version with Ubuntu.
Since the official protobuf source code files (located at twsapi_macunix.1037.02/IBJts/source/cppclient/client/protobufUnix
) are generated with version 3.12.4, we should regenerate the original proto files (in twsapi_macunix.1037.02/IBJts/source/proto
) with protobuf 3.21.12. This is because these versions have different APIs, and the officially generated files cannot be used with protobuf 3.21.12.
Command:
cd ~/Downloads/twsapi_macunix.1037.02/IBJts/source
protoc --proto_path=./proto --experimental_allow_proto3_optional --cpp_out=$HOME/Downloads/proto proto/*.proto
Then, we’ll replace all files in ~/Downloads/twsapi_macunix.1037.02/IBJts/source/cppclient/client/protobufUnix/
with those in ~/Downloads/proto/
Find libprotobuf
I prefer to use static libraries (.a
files) because they’re usually self-contained and don’t require external libraries to be installed, compared to shared libraries. Since we don’t change their versions at all, it’s safe to use static libraries.
libprotobuf.a
is located at:
Ubuntu: /usr/lib/x86_64-linux-gnu/libprotobuf.a
macOS: /opt/homebrew/Cellar/protobuf@21/21.12_1/lib/libprotobuf.a
Header files are located at:
Ubuntu: /usr/include/google/protobuf/
macOS: /opt/homebrew/Cellar/protobuf@21/21.12_1/include/google/protobuf
If you want to copy header files to your project, you’ll usually copy the google
folder instead of protobuf
because the generated proto source code files use includes like <google/protobuf/xxx>
Building protobuf from source code
If you really want to build protobuf from source code, I suggest git cloning the official repository and following the specific version’s build guide.
For example, if you want to use IBKR suggested version 3.12.4, use such command:
git clone https://github.com/protocolbuffers/protobuf -b v3.12.4
and follow its documentation to build it.
Or, you may download pre-built packages from https://github.com/protocolbuffers/protobuf/releases/tag/v3.12.4
Building Intel Decimal Floating-Point Math Library
Configuration
In Linux (x86_64)
This part is not too difficult. IBKR provides clear steps to build the library, which can be found in twsapi_macunix.1037.02/IBJts/source/cppclient/Intel_lib_build.txt
(directly usable on Linux x86_64):
1) Extract [Intel Decimal Floating Point Library] (https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html)
to some directory
2) In IntelRDFPMathLib20U2/LIBRARY modify "makefile":
a)
Line 370:
change
BID_LIB = $(LIB_DIR)/libbid.$A
to
BID_LIB = $(LIB_DIR)/libbid.so
b)
Line 377:
change
$(AR_CMD) $(AR_OUT)$@ $^
to
gcc -o $@ $^ -shared
c)
Line 112:
change
_CFLAGS_OPT :=
to
_CFLAGS_OPT := -fPIC
2) In IntelRDFPMathLib20U2/LIBRARY execute the following commands:
make CC=gcc CALL_BY_REF=0 GLOBAL_RND=0 GLOBAL_FLAGS=0 UNCHANGED_BINARY_FLAGS=0
rm *.o
Note: in the sample above all build flags are set to 0. See IntelRDFPMathLib20U2/README for details.
3) As result there is libbid.so library generated. You can use it with TestCppClient.
In macOS or Linux (aarch64)
If you want to build it in macOS, you’ll modify some parameters. You may reference this article:
IntelRDFPMathLib20U2/LIBRARY modify "makefile":
a)
Line 370:
change
BID_LIB = $(LIB_DIR)/libbid.$A
to
BID_LIB = $(LIB_DIR)/libbid.dylib # or .so when building on Linux
b)
Line 377:
change
$(AR_CMD) $(AR_OUT)$@ $^
to
gcc -o $@ $^ -shared
c)
Line 112:
change
_CFLAGS_OPT :=
to
_CFLAGS_OPT := -fPIC -Wno-implicit-function-declaration # added to avoid issues with newer versions of clang and gcc (>=14), which now treat implicit function declarations as an error
And also:
# add aarch64 with EFI2 specified for both ARCH_LIST and ARCH_TYPE
ARCH_ALIAS := x86 ia64 EM64T x86_64 i686 amd64 Intel64 sun4u aarch64
ARCH_LIST := IA32 IA64 EFI2 EFI2 IA32 EFI2 EFI2 EFI2 EFI2
ARCH_TYPE := IA32 IA64 EFI2 EFI2 IA32 EFI2 EFI2 EFI2 EFI2
ARCH_TYPES := IA32 IA64 EFI2
Building
Once you’ve updated the makefile
, you can successfully compile the Intel Decimal Library without errors using the following command:
make CC=gcc CALL_BY_REF=0 GLOBAL_RND=0 GLOBAL_FLAGS=0 UNCHANGED_BINARY_FLAGS=0
Then you’ll find libbid.dylib (macOS) or libbid.so (Linux)
Building IBKR C++ library
This part is a bit tricky. First, you’ll create a lib
folder under twsapi_macunix.1037.02/IBJts/source/cppclient/client
and copy libbid.dylib (macOS) or libbid.so (Linux) to it. You’ll have to copy libprotobuf.so/libprotobuf.a to this folder if you built it from source code or simply downloaded pre-built versions.
~/Downloads/twsapi_macunix.1037.02/IBJts/source/cppclient/client
❯ mkdir lib
~/Downloads/twsapi_macunix.1037.02/IBJts/source/cppclient/client
❯ cp ~/Downloads/IntelRDFPMathLib20U2/LIBRARY/libbid.dylib lib/
~/Downloads/twsapi_macunix.1037.02/IBJts/source/cppclient/client
❯ ll lib/libbid.dylib
-rwxr-xr-x@ 1 zach staff 4.7M 31 Aug 22:22 lib/libbid.dylib
Remember, I wanted to build a static library? Here’s my updated makefile
(macOS):
CXX=g++
CXXFLAGS_BASE=-pthread -Wall -Wno-switch -Wpedantic -Wno-unused-function -std=c++11 -fPIC
CXXFLAGS_SHARED=$(CXXFLAGS_BASE) -shared
CXXFLAGS_STATIC=$(CXXFLAGS_BASE)
ROOT_DIR=.
BASE_SRC_DIR=${ROOT_DIR}
PROTOBUF_DIR=$(BASE_SRC_DIR)/protobufUnix
# Manual protobuf paths (adjust if installed elsewhere)
PROTOBUF_INCLUDE=/opt/homebrew/opt/protobuf@21/include
PROTOBUF_LIB=/opt/homebrew/opt/protobuf@21/lib
# Combine includes
INCLUDES=-I${ROOT_DIR} -I${PROTOBUF_DIR} -I$(PROTOBUF_INCLUDE)
LIB_DIR=lib
LIB_NAME=libbid.dylib
TARGET_SHARED=libTwsSocketClient.dylib
TARGET_STATIC=libTwsSocketClient.a
# Default target
all: shared
# Shared library
shared: $(TARGET_SHARED)
$(TARGET_SHARED):
$(CXX) $(CXXFLAGS_SHARED) $(INCLUDES) $(BASE_SRC_DIR)/*.cpp ${PROTOBUF_DIR}/*.cc \
-L$(LIB_DIR) -lbid -L$(PROTOBUF_LIB) -lprotobuf -o$(TARGET_SHARED)
# Static library
static: $(TARGET_STATIC)
$(TARGET_STATIC):
$(CXX) $(CXXFLAGS_STATIC) $(INCLUDES) -c $(BASE_SRC_DIR)/*.cpp ${PROTOBUF_DIR}/*.cc
ar rcs $(TARGET_STATIC) *.o
rm -f *.o
clean:
rm -f $(TARGET_SHARED) $(TARGET_STATIC) *.o
# Build both
both: shared static
And Ubuntu (x86_64):
CXX=g++
CXXFLAGS_BASE=-pthread -Wall -Wno-switch -Wpedantic -Wno-unused-function -std=c++11 -fPIC
ROOT_DIR=.
BASE_SRC_DIR=${ROOT_DIR}
PROTOBUF_DIR=$(BASE_SRC_DIR)/protobufUnix
# Add protobuf flags
PROTOBUF_CFLAGS=$(shell pkg-config --cflags protobuf)
PROTOBUF_LIBS=$(shell pkg-config --libs protobuf)
# Update INCLUDES to include protobuf paths
INCLUDES=-I${ROOT_DIR} -I${PROTOBUF_DIR} $(PROTOBUF_CFLAGS)
LIB_DIR=lib
LIB_NAME=libbid.so
TARGET_SHARED=libTwsSocketClient.so
TARGET_STATIC=libTwsSocketClient.a
# Installation directories
PREFIX ?= /root/ibkr
INSTALL_LIB_DIR = $(PREFIX)/lib
INSTALL_INCLUDE_DIR = $(PREFIX)/include/TwsSocketClient
# Header files to install
HEADERS = $(wildcard $(BASE_SRC_DIR)/*.h) \
$(wildcard $(PROTOBUF_DIR)/*.h)
# Default target
all: both
# Shared library
shared: $(TARGET_SHARED)
$(TARGET_SHARED):
$(CXX) $(CXXFLAGS_BASE) -shared $(INCLUDES) $(BASE_SRC_DIR)/*.cpp ${PROTOBUF_DIR}/*.cc \
-L$(LIB_DIR) -l:$(LIB_NAME) -o$(TARGET_SHARED) $(PROTOBUF_LIBS)
# Static library
static: $(TARGET_STATIC)
$(TARGET_STATIC):
$(CXX) $(CXXFLAGS_BASE) $(INCLUDES) -c $(BASE_SRC_DIR)/*.cpp ${PROTOBUF_DIR}/*.cc
ar rcs $(TARGET_STATIC) *.o
# Build both
both: shared static
# Install libraries and headers
install: both
@echo "Installing libraries to $(INSTALL_LIB_DIR)"
@mkdir -p $(INSTALL_LIB_DIR)
@cp $(TARGET_SHARED) $(TARGET_STATIC) $(INSTALL_LIB_DIR)/
@echo "Installing headers to $(INSTALL_INCLUDE_DIR)"
@mkdir -p $(INSTALL_INCLUDE_DIR)
@cp $(BASE_SRC_DIR)/*.h $(INSTALL_INCLUDE_DIR)/
@mkdir -p $(INSTALL_INCLUDE_DIR)/protobufUnix
@cp $(PROTOBUF_DIR)/*.h $(INSTALL_INCLUDE_DIR)/protobufUnix/ 2>/dev/null || true
@echo "Installation complete!"
@echo "Libraries installed to: $(INSTALL_LIB_DIR)"
@echo "Headers installed to: $(INSTALL_INCLUDE_DIR)"
# Uninstall
uninstall:
rm -f $(INSTALL_LIB_DIR)/$(TARGET_SHARED)
rm -f $(INSTALL_LIB_DIR)/$(TARGET_STATIC)
rm -rf $(INSTALL_INCLUDE_DIR)
# Clean
clean:
rm -f $(TARGET_SHARED) $(TARGET_STATIC) *.o $(BASE_SRC_DIR)/*.o $(PROTOBUF_DIR)/*.o
.PHONY: all shared static both clean install uninstall
Note that I set the export folder as PREFIX ?= /root/ibkr
in Linux so that once I built it, the exported headers and built packages are located in /root/ibkr
. You can set it in the macOS version as well.
And then, you can simply run make both
to build both shared and static libraries:
libTwsSocketClient.a
(static library on both macOS and Linux, but they are two different files with the same name and .a suffix)libTwsSocketClient.dylib
(macOS shared library)libTwsSocketClient.so
(Linux shared library)
Next
Next? You’ve got header files and libraries in /root/ibkr
already. You can start with either compiling examples given by IBKR or building your own trading program.
I’ll write the Windows version in the next post. The Windows version is slightly different.