ib-dx
Talk directly to IBKR. The TWS API your program already uses, with no IB Gateway, no Trader Workstation and no JVM between you and the venue.
For ib_async, use ib_async-dx, which runs it on this engine.
One line changes
A program written against the TWS API talks to IB Gateway or Trader Workstation over a socket on localhost, and that process talks to the venue. ib-dx takes the gateway's place: it logs in, holds the trading, market-data, historical and security-definition connections open, and gives your program the same calls and the same callbacks.
- client.connect("127.0.0.1", 4001, clientId=1) # needs a gateway running
+ client.connect(username="...", password="...") # no external process
port is still accepted, and client.port reads it back while the session is
held, but nothing connects on it. There is no local process to point it at.
What goes away
The process
Nothing to install, launch, log into on a schedule, or restart.
The JVM
No Java runtime to install, and no heap to size.
The localhost socket
Callbacks are delivered in-process, on the thread that runs the dispatch: run() or poll() in Python, process_msgs in Rust.
The window
It runs headless: in a container, over ssh, on a machine with no display.
What you get
The shape you already have
EClient and EWrapper in Python, EClient and Wrapper in Rust, with the TWS API's method names and callbacks.
One engine, two languages
The Python module is the Rust core through PyO3, not a second implementation. A gate on every commit fails if the two surfaces stop agreeing on a call or a callback the TWS API documents.
Nothing in between
The engine runs on a thread of its own and can be pinned to a core. Quotes are published through a seqlock: the writer never blocks, and a reader takes a whole quote without a lock.
More than the API forwards
The session states things a gateway never passes on: the account's grants, the order types it will take, its algorithms, and data series with no documented call. See what.
Honest about its limits
A call the protocol cannot carry says why, instead of returning as though it acted. The limits are written down, over a coverage matrix regenerated from the source on every commit, and what a gateway answers the same way is on venue behaviour.
Where to go next
Status
Under active development. Every capability claim in this repository is assigned from a named artifact, a test, a script or a recorded server response, never from reading the code. The matrix is in capabilities.md, its counts are recomputed on every commit, and the build fails if one moves.
Both install routes build from the repository: see Getting started.