4 minute read

Recently, I wanted to use the C++ API client library that Interactive Brokers provides and experiment with some algorithmitic trading and monitoring of my positions. I had hoped there would be some precompiled binaries already for MacOS & Linux/aarch64; however, I did not have luck finding any. That is not a problem since Interactive Brokers provides the source to the client libraries under a license that allows for non-commercial use. The source can be downloaded here after agreeing to the license.

I downloaded the Latest release of the library for Mac/Unix, which at the time of this writing is version API 10.34. Once downloaded, I extracted the .zip file:

~/Downloads $ unzip -d twsapi_macunix.1034.02 twsapi_macunix.1034.02.zip

The build documentation for Mac/Unix is available here. The documentation is mostly focused on the building the Python library and the C++ library build documentation is lacking. The API’s Decimal implementation also has an odd dependency on the Intel Decimal Library, which was concerning since I wanted to build this for MacOS on Apple Silicon and Linux/aarch64. I assumed the Intel library is Intel-specific, but much to my pleasant surprise it is not.

After downloading the Intel Decimal Floating-Point Math Library from: https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html, I extracted that tarball:

Building the Intel Decimal Floating-Point Math Library

~/Downloads $ tar xvzf IntelRDFPMathLib20U2.tar.gz

Interactive Brokers provides steps to build the library contained in IBJts/source/cppclient/Intel_lib_build.txt, which I followed with some necessary modifications below:

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

If building on Linux/aarch64, the following error will be encountered about an unknown architecture:

[dlewis@defiant LIBRARY]$ make
makefile.iml_head:356: *** Unknown host architecture aarch64.  Stop.

This can be easily fixed by making some small changes to the makefile.iml_head file around line 347:

# 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

Now, the Intel Decimal Library can be built without issues on MacOS or Linux/aarch64:

~/Downloads/IntelRDFPMathLib20U2/LIBRARY $ make CC=gcc CALL_BY_REF=0 GLOBAL_RND=0 GLOBAL_FLAGS=0 UNCHANGED_BINARY_FLAGS=0

Once the build finishes, there will be a .dylib/.so:

~/Downloads/IntelRDFPMathLib20U2/LIBRARY $ ls -l libbid.dylib 
-rwxr-xr-x  1 dlewis  staff  5019408 Feb 11 14:39 libbid.dylib
~/Downloads/IntelRDFPMathLib20U2/LIBRARY $ file libbid.dylib 
libbid.dylib: Mach-O 64-bit dynamically linked shared library arm64

Building the IBKR C++ API Client Library

Back to building the IBKR C++ API client library. The libbid.dylib/.so needs to be copied to IBJts/source/cppclient/client/lib:

~/Downloads/twsapi_macunix.1034.02/IBJts/source/cppclient/client $ mkdir lib
~/Downloads/twsapi_macunix.1034.02/IBJts/source/cppclient/client $ cp ~/Downloads/IntelRDFPMathLib20U2/LIBRARY/libbid.dylib lib/
~/Downloads/twsapi_macunix.1034.02/IBJts/source/cppclient/client $ ls -l lib/libbid.dylib
-rwxr-xr-x  1 dlewis  staff  5019408 Feb 11 14:44 lib/libbid.dylib

I had many issues building with CMake in IBJts/source/cppclient/client, and the sources include a generic makefile, which seemed much easier to modify, so I went that route instead. Here’s my slightly modified makefile:

CXX=g++
CXXFLAGS=-pthread -Wall -Wno-switch -Wno-unused-function -std=c++11 -shared -fPIC
ROOT_DIR=.
BASE_SRC_DIR=${ROOT_DIR}
INCLUDES=-I${ROOT_DIR}
LIB_DIR=lib
LIB_NAME=bid
TARGET=libTwsSocketClient.dylib # or .so if building on Linux

$(TARGET):
	$(CXX) $(CXXFLAGS) $(INCLUDES) $(BASE_SRC_DIR)/*.cpp -L$(LIB_DIR) -l$(LIB_NAME) -o$(TARGET)

clean:
	rm -f $(TARGET) *.o

Once that is modified, make can simply be run without issue:

~/Downloads/twsapi_macunix.1034.02/IBJts/source/cppclient/client $ make
g++ -pthread -Wall -Wno-switch -Wno-unused-function -std=c++11 -shared -fPIC -I. ./*.cpp -Llib -lbid -olibTwsSocketClient.dylib

In preparation for building the sample client IBKR provides, I copied libTwsSocketClient.dylib to lib, where previously libbid.dylib was copied to:

~/Downloads/twsapi_macunix.1034.02/IBJts/source/cppclient/client $ cp libTwsSocketClient.dylib lib/
~/Downloads/twsapi_macunix.1034.02/IBJts/source/cppclient/client $ ls -l lib/
total 12200
-rwxr-xr-x  1 dlewis  staff  1221496 Feb 11 14:57 libTwsSocketClient.dylib
-rwxr-xr-x  1 dlewis  staff  5019408 Feb 11 14:44 libbid.dylib

Building the C++ Sample Application

Now, the client can be built in IBJts/samples/Cpp/TestCppClient. First, some similar makefile modifications need to be made, and again I avoided using CMake because of problems encountered with the build process. It can be made to work, but it is much simpler to just use the provided makefile. Here is my diff of the makefile after modifications:

~/Downloads/twsapi_macunix.1034.02/IBJts/samples/Cpp/TestCppClient $ diff -u makefile makefile.orig
--- makefile	2025-02-11 15:03:37
+++ makefile.orig	2025-02-11 15:02:31
@@ -4,10 +4,10 @@
 BASE_SRC_DIR=${ROOT_DIR}/client
 INCLUDES=-I${BASE_SRC_DIR} -I${ROOT_DIR}
 SOURCE_DIR=${BASE_SRC_DIR}
-SOURCE_LIB=TwsSocketClient
+SOURCE_LIB=libTwsSocketClient.so
 LIB_DIR=$(SOURCE_DIR)/lib
 LIB_NAME_A=libbid.a
-LIB_NAME=bid
+LIB_NAME_SO=libbid.so
 TARGET=TestCppClient
 
 $(TARGET)Static:
@@ -17,7 +17,7 @@
 	$(CXX) $(CXXFLAGS) $(INCLUDES) $(BASE_SRC_DIR)/*.cpp ./*.cpp -L$(LIB_DIR) -l:$(LIB_NAME_SO) -o$(TARGET)
 
 $(TARGET)Dynamic:
-	$(CXX) $(CXXFLAGS) $(INCLUDES)  ./*.cpp -L$(LIB_DIR) -l$(LIB_NAME) -L$(SOURCE_DIR) -l$(SOURCE_LIB) -o$(TARGET)Dynamic
+	$(CXX) $(CXXFLAGS) $(INCLUDES)  ./*.cpp -L$(LIB_DIR) -l:$(LIB_NAME_SO) -L$(SOURCE_DIR) -l:$(SOURCE_LIB) -o$(TARGET)Dynamic
 
 run$(TARGET)Static:
 	./$(TARGET)Static

The makefile can be easily modified or used to generate static application binaries, but in this case I opted for dynamic linking. If you go the statically linked route, then you can also easily make the necessary modifications to the C++ API library makefile in

Finally, we can build and run the TestCppClientDynamic:

~/Downloads/twsapi_macunix.1034.02/IBJts/samples/Cpp/TestCppClient $ make TestCppClientDynamic
g++ -pthread -Wall -Wno-switch -Wpedantic -Wno-unused-function -std=c++11 -I../../../source/cppclient/client -I../../../source/cppclient  ./*.cpp -L../../../source/cppclient/client/lib -lbid -L../../../source/cppclient/client -lTwsSocketClient -oTestCppClientDynamic
~/Downloads/twsapi_macunix.1034.02/IBJts/samples/Cpp/TestCppClient $ DYLD_LIBRARY_PATH=../../../source/cppclient/client/lib ./TestCppClientDynamic 
Start of C++ Socket Client Test 0
Attempt 1 of 50
Connecting to 127.0.0.1:7496 clientId:0
Error. Id: -1, Time: Tue Feb 11 15:08:17 2025, Code: 502, Msg: Couldn't connect to TWS. Confirm that "Enable ActiveX and Socket Clients" is enabled and connection port is the same as "Socket Port" on the TWS "Edit->Global Configuration...->API->Settings" menu. Live Trading ports: TWS: 7496; IB Gateway: 4001. Simulated Trading ports for new installations of version 954.1 or newer:  TWS: 7497; IB Gateway: 4002
Cannot connect to 127.0.0.1:7496 clientId:0
Sleeping 10 seconds before next attempt

Success! Hopefully, this will be helpful to someone looking to build the IBKR C++ API library on MacOS or Linux/aarch64.